UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

131 lines (119 loc) 4.09 kB
/** * A solver re-uses its output as its input on each iteration * */ import {BaseNodeType} from './../_Base'; import {SolverIterationStamp} from './utils/SolverIterationStamp'; import {NetworkNodeType} from './../../poly/NodeContext'; import {SubnetSopNodeLike, SopSubnetChildrenDisplayController} from './utils/subnet/SopSubnetChildrenDisplayController'; import {InputCloneMode} from '../../poly/InputCloneMode'; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; import {CoreGroup} from '../../../core/geometry/Group'; import {DisplayNodeController} from '../utils/DisplayNodeController'; class SolverSopParamsConfig extends NodeParamsConfig { /** @param number of times the nodes inside this node will process the input */ iterations = ParamConfig.INTEGER(2, { range: [0, 5], rangeLocked: [true, false], }); /** @param Currently, when the child nodes are updated, the solver node does not know that it should recook. Clicking this button forces it to recompute */ reload = ParamConfig.BUTTON(null, { callback: (node: BaseNodeType) => { SolverSopNode.PARAM_CALLBACK_reload(node as SolverSopNode); }, }); } const ParamsConfig = new SolverSopParamsConfig(); export class SolverSopNode extends SubnetSopNodeLike<SolverSopParamsConfig> { override paramsConfig = ParamsConfig; static override type() { return NetworkNodeType.SOLVER; } private _iterationStamp: SolverIterationStamp | undefined; public override readonly childrenDisplayController: SopSubnetChildrenDisplayController = new SopSubnetChildrenDisplayController(this, {dependsOnDisplayNode: false}); public override readonly displayNodeController: DisplayNodeController = new DisplayNodeController( this, { onDisplayNodeRemove: () => { // this.node.setDirty(); }, onDisplayNodeSet: () => { // this.node.setDirty(); }, onDisplayNodeUpdate: () => { // this.node.setDirty(); }, }, {dependsOnDisplayNode: false} ); override initializeNode() { this.io.inputs.setCount(0, 4); this.io.inputs.initInputsClonedState(InputCloneMode.NEVER); } iterationStamp() { return (this._iterationStamp = this._iterationStamp || this._createStampNode()); } private _createStampNode() { const stampNode = new SolverIterationStamp(this.scene()); // this.dirtyController.setForbiddenTriggerNodes([stampNode]); stampNode.setForbiddenTriggerNodes(this); return stampNode; } private _previousFrameCoreGroup: CoreGroup | undefined; previousFrameCoreGroup() { return this._previousFrameCoreGroup; } override async cook(inputCoreGroups: CoreGroup[]) { this._reset(); if (this.pv.iterations == 0) { this.setCoreGroup(inputCoreGroups[0]); return; } await this._computeSolverMultipleTimes(this.pv.iterations); } private _reset() { this._previousFrameCoreGroup = undefined; if (this.iterationStamp().iteration() == 0) { this.iterationStamp().setIteration(-1); } } private async _computeSolverMultipleTimes(iterationsCount: number) { for (let i = 0; i < iterationsCount; i++) { this.iterationStamp().setIteration(i); await this.computeSolver(i == iterationsCount - 1); } } private async computeSolver(isLastIteration: boolean) { const childOutputNode = this.childrenDisplayController.outputNode(); let coreContent: CoreGroup | undefined; if (childOutputNode) { const container = await childOutputNode.compute(); coreContent = container.coreContent(); if (coreContent) { this._previousFrameCoreGroup = coreContent; } else { if (childOutputNode.states.error.active()) { this.states.error.set(childOutputNode.states.error.message()); } else { this._previousFrameCoreGroup = undefined; } } } else { this.states.error.set('no output node found inside subnet'); } if (isLastIteration) { if (coreContent) { this.setCoreGroup(coreContent); } else { this.setObjects([]); } } } static PARAM_CALLBACK_reload(node: SolverSopNode) { node.param_callback_reload(); } private param_callback_reload() { this.p.iterations.setDirty(); } }