polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
305 lines (284 loc) • 9.6 kB
text/typescript
import {CoreWalker} from '../../core/Walker';
import {CoreGraphNode} from '../../core/graph/CoreGraphNode';
import {BaseNodeType} from '../nodes/_Base';
import {OptionsController} from './utils/OptionsController';
import {ExpressionController} from './utils/ExpressionController';
import {EmitController} from './utils/EmitController';
import {ParamSerializer} from './utils/Serializer';
import {StatesController} from './utils/StatesController';
import {TypedMultipleParam} from './_Multiple';
import {FloatParam} from './Float';
import {ParamType} from '../poly/ParamType';
import {ParamEvent} from '../poly/ParamEvent';
import {PolyScene} from '../scene/PolyScene';
import {ParamInitValuesTypeMap} from '../params/types/ParamInitValuesTypeMap';
import {ParamValuesTypeMap} from '../params/types/ParamValuesTypeMap';
import {
ParamValueSerializedTypeMap,
ParamValuePreConversionSerializedTypeMap,
} from '../params/types/ParamValueSerializedTypeMap';
import {ParamInitValueSerializedTypeMap} from './types/ParamInitValueSerializedTypeMap';
import {MethodDependency} from '../expressions/MethodDependency';
type ComputeCallback = (value: void) => void;
const TYPED_PARAM_DEFAULT_COMPONENT_NAMES: Readonly<string[]> = [];
export abstract class TypedParam<T extends ParamType> extends CoreGraphNode {
protected _default_value!: ParamInitValuesTypeMap[T];
protected _raw_input!: ParamInitValuesTypeMap[T];
protected _value!: ParamValuesTypeMap[T];
protected _node: BaseNodeType;
protected _parent_param: TypedMultipleParam<any> | undefined;
protected _components: FloatParam[] | undefined;
protected _compute_resolves: ComputeCallback[] | undefined;
private _options: OptionsController = new OptionsController(this);
get options(): OptionsController {
return (this._options = this._options || new OptionsController(this));
}
private _emit_controller: EmitController = new EmitController(this);
get emitController(): EmitController {
return (this._emit_controller = this._emit_controller || new EmitController(this));
}
protected _expression_controller: ExpressionController<T> | undefined;
get expression_controller(): ExpressionController<T> | undefined {
return this._expression_controller; // =
//this._expression_controller || new ExpressionController(this);
}
private _serializer: ParamSerializer | undefined;
get serializer(): ParamSerializer {
return (this._serializer = this._serializer || new ParamSerializer(this));
}
private _states: StatesController | undefined;
get states(): StatesController {
return (this._states = this._states || new StatesController(this));
}
// private _ui_data: UIData | undefined;
// get ui_data(): UIData {
// return (this._ui_data = this._ui_data || new UIData(this.scene, this));
// }
constructor(scene: PolyScene, node: BaseNodeType) {
super(scene, 'BaseParam');
this._node = node;
this.initialize_param();
}
dispose() {
// if any direct predecessor is a MethodDependency,
// it must be disposed here
const predecessors = this.graphPredecessors();
for (let predecessor of predecessors) {
if (predecessor instanceof MethodDependency) {
predecessor.dispose();
}
}
this._expression_controller?.dispose();
super.dispose();
this._options?.dispose();
}
initialize_param() {}
// // this.addPostDirtyHook(this._remove_node_param_cache.bind(this))
// }
// initialize() {
// this.init_components();
// // this.init_expression()
// // this._init_ui_data()
// }
// accepts_visitor<T extends ParamVisitor>(visitor: T): ReturnType<T['visit_param']> {
// return visitor.visit_param(this);
// }
//
// init_expression() {}
// type
static type(): ParamType {
return ParamType.FLOAT; // adding a type here, but just to not have a compile error
}
type(): T {
return (this.constructor as typeof BaseParamClass).type() as T;
}
get is_numeric(): boolean {
return false;
}
// name
setName(name: string) {
super.setName(name);
}
get value(): ParamValuesTypeMap[T] {
return this._value;
}
abstract get default_value_serialized(): ParamInitValueSerializedTypeMap[T];
abstract get raw_input_serialized(): ParamInitValueSerializedTypeMap[T];
abstract get value_serialized(): ParamValueSerializedTypeMap[T];
copy_value(param: BaseParamType) {
if (param.type() == this.type()) {
this._copy_value(param as TypedParam<T>);
} else {
console.warn(`cannot copy value from ${param.type()} to ${this.type()}`);
}
}
protected _copy_value(param: TypedParam<T>) {
throw 'abstract method param._copy_value';
}
get value_pre_conversion_serialized(): ParamValuePreConversionSerializedTypeMap[T] {
return undefined as never;
}
convert(raw_val: any): ParamValuesTypeMap[T] | null {
return null;
}
static are_raw_input_equal(val1: any, val2: any) {
return false;
}
is_raw_input_equal(other_raw_input: ParamInitValuesTypeMap[T]) {
return (this.constructor as any).are_raw_input_equal(this._raw_input, other_raw_input);
}
static are_values_equal(val1: any, val2: any) {
return false;
}
is_value_equal(other_val: ParamValuesTypeMap[T]) {
return (this.constructor as any).are_values_equal(this.value, other_val);
}
protected _clone_raw_input(raw_input: ParamInitValuesTypeMap[T]): ParamInitValuesTypeMap[T] {
return raw_input;
}
set(raw_input: ParamInitValuesTypeMap[T]): void {
this._raw_input = this._clone_raw_input(this._prefilter_invalid_raw_input(raw_input));
this.emitController.emit(ParamEvent.RAW_INPUT_UPDATED);
this.process_raw_input();
}
protected _prefilter_invalid_raw_input(raw_input: any): ParamInitValuesTypeMap[T] {
return raw_input as ParamInitValuesTypeMap[T];
}
get default_value() {
return this._default_value;
}
get is_default(): boolean {
return this._raw_input == this.default_value;
}
get raw_input() {
return this._raw_input;
}
protected process_raw_input() {}
private _is_computing: boolean = false;
async compute(): Promise<void> {
if (this.scene().loadingController.isLoading()) {
console.warn(`param attempt to compute ${this.fullPath()}`);
}
if (this.isDirty()) {
if (!this._is_computing) {
this._is_computing = true;
await this.process_computation();
this._is_computing = false;
if (this._compute_resolves) {
let callback: ComputeCallback | undefined;
while ((callback = this._compute_resolves.pop())) {
callback();
}
}
} else {
return new Promise((resolve, reject) => {
this._compute_resolves = this._compute_resolves || [];
this._compute_resolves.push(resolve);
});
}
}
}
protected async process_computation(): Promise<void> {}
// set_default_value(default_value: ParamValuesTypeMap[T]) {
// this._default_value = default_value;
// }
set_init_value(init_value: ParamInitValuesTypeMap[T]) {
this._default_value = this._clone_raw_input(this._prefilter_invalid_raw_input(init_value));
// this._raw_input = this._clone_raw_input(init_value);
// if (this.is_multiple) {
// this.init_components();
// }
// this.set(init_value);
}
// eval_p(): Promise<ParamValuesTypeMap[T]> {
// return new Promise((resolve, reject) => {
// resolve();
// });
// }
// node
_setup_node_dependencies(node: BaseNodeType | null) {
if (!node) {
if (this._node) {
this._node.params.params_node?.removeGraphInput(this);
}
} else {
// allow callbacks after the value is set,
// so that the param does not trigger the node to recompute
// before all params are added
this.options.allowCallback();
if (this.options.makes_node_dirty_when_dirty() && !this.parent_param) {
node.params.params_node?.addGraphInput(this, false);
}
}
if (this.components) {
for (let c of this.components) {
c._setup_node_dependencies(node);
}
}
}
get node() {
return this._node;
}
parent() {
return this.node;
}
// hierarchy
set_parent_param(param: TypedMultipleParam<any>) {
param.addGraphInput(this, false);
this._parent_param = param;
}
get parent_param(): TypedMultipleParam<any> | undefined {
return this._parent_param;
}
has_parent_param(): boolean {
return this._parent_param != null;
}
fullPath(): string {
return this.node?.fullPath() + '/' + this.name();
}
path_relative_to(node: BaseNodeType | BaseParamType): string {
return CoreWalker.relative_path(node, this);
}
// emit
emit(event_name: ParamEvent): void {
if (this.emitController.emitAllowed()) {
this.emitController.incrementCount(event_name);
this.scene().dispatchController.dispatch(this, event_name);
}
}
// multiple
get components() {
return this._components;
}
get component_names(): Readonly<string[]> {
return TYPED_PARAM_DEFAULT_COMPONENT_NAMES;
}
get is_multiple(): boolean {
return this.component_names.length > 0;
}
// create_components() {}
init_components() {}
// expression
// set_expression(expression: string | null) {
// this.expression_controller.set_expression(expression);
// }
has_expression(): boolean {
return this.expression_controller != null && this.expression_controller.active(); // use this._expression_controller to avoid creating it
}
// serialize
toJSON() {
return this.serializer.toJSON();
}
}
export type BaseParamType = TypedParam<ParamType>;
export class BaseParamClass extends TypedParam<ParamType> {
get default_value_serialized() {
return 'BaseParamClass.default_value_serialized overriden';
}
get raw_input_serialized() {
return 'BaseParamClass.raw_input_serialized overriden';
}
get value_serialized() {
return 'BaseParamClass.value_serialized overriden';
}
}