UNPKG

@polygonjs/polygonjs

Version:

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

135 lines (121 loc) 4.84 kB
/** * Transfers an attribute from right input to left input * * @remarks * This can be useful to create heatmap. * */ import {TypedSopNode} from './_Base'; import type {BaseCorePoint, CorePoint} from '../../../core/geometry/entities/point/CorePoint'; import {CoreGroup} from '../../../core/geometry/Group'; import {CoreInterpolate} from '../../../core/math/Interpolate'; import {CoreOctree} from '../../../core/math/octree/Octree'; import {CoreIterator} from '../../../core/Iterator'; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; import {InputCloneMode} from '../../poly/InputCloneMode'; import {Box3, Vector3} from 'three'; import {SopType} from '../../poly/registers/nodes/types/Sop'; import {corePointClassFactory} from '../../../core/geometry/CoreObjectFactory'; import {CoreObjectType, ObjectContent} from '../../../core/geometry/ObjectContent'; import {pointsFromObjectFromGroup} from '../../../core/geometry/entities/point/CorePointUtils'; const _tmpBox = new Box3(); const _position = new Vector3(); const _nearestPoints: BaseCorePoint[] = []; const _pointsSrc: CorePoint<CoreObjectType>[] = []; class AttribTransferSopParamsConfig extends NodeParamsConfig { /** @param source group to transfer from (right input, or input 1) */ srcGroup = ParamConfig.STRING(); /** @param dest group to transfer to (left input, or input 0) */ destGroup = ParamConfig.STRING(); /** @param name of the attribute to transfer */ name = ParamConfig.STRING(); /** @param max number of samples to use */ maxSamplesCount = ParamConfig.INTEGER(1, { range: [1, 10], rangeLocked: [true, false], }); /** @param max distance to search points to transfer from */ distanceThreshold = ParamConfig.FLOAT(1); /** @param blend width */ blendWidth = ParamConfig.FLOAT(0); } const ParamsConfig = new AttribTransferSopParamsConfig(); export class AttribTransferSopNode extends TypedSopNode<AttribTransferSopParamsConfig> { override paramsConfig = ParamsConfig; static override type() { return SopType.ATTRIB_TRANSFER; } override initializeNode() { this.io.inputs.setCount(2); this.io.inputs.initInputsClonedState([InputCloneMode.FROM_NODE, InputCloneMode.NEVER]); } override async cook(inputCoreGroups: CoreGroup[]) { const coreGroupDest = inputCoreGroups[0]; const coreGroupSrc = inputCoreGroups[1]; // build octree coreGroupSrc.pointsFromGroup(this.pv.srcGroup, _pointsSrc); coreGroupSrc.boundingBox(_tmpBox); const octree = new CoreOctree(_tmpBox); octree.setPoints(_pointsSrc); // transfer const destObjects = coreGroupDest.allObjects(); const srcObjects = coreGroupSrc.allObjects(); let i = 0; for (const destObject of destObjects) { const srcObject = srcObjects[i]; const corePointClass = corePointClassFactory(destObject); const attributeNames = corePointClass.attributeNamesMatchingMask(srcObject, this.pv.name); this._addAttributeIfRequired(destObject, srcObject, attributeNames); await this._transferAttributes(destObject, octree, attributeNames); i++; } this.setCoreGroup(coreGroupDest); } private async _transferAttributes<T extends CoreObjectType>( object: ObjectContent<T>, octree: CoreOctree, attribNames: string[] ) { const callback = (destPoint: BaseCorePoint) => { this._transferAttributesForPoint(destPoint, octree, attribNames); }; const destPoints: CorePoint<CoreObjectType>[] = []; pointsFromObjectFromGroup(object, this.pv.destGroup, destPoints); const _iterator = new CoreIterator(); await _iterator.startWithArray(destPoints, callback); } private _addAttributeIfRequired<T extends CoreObjectType>( destObject: ObjectContent<T>, srcObject: ObjectContent<T>, attribNames: string[] ) { for (const attribName of attribNames) { const corePointClass = corePointClassFactory(destObject); const hasAttrib = corePointClass.hasAttribute(destObject, attribName); if (!hasAttrib) { const attribSize = corePointClass.attribSize(srcObject, attribName); corePointClass.addNumericAttribute(destObject, attribName, attribSize, 0); } } } private _transferAttributesForPoint(destPoint: BaseCorePoint, octree: CoreOctree, attribNames: string[]) { const totalDist = this.pv.distanceThreshold + this.pv.blendWidth; destPoint.position(_position); octree.findPoints(_position, totalDist, this.pv.maxSamplesCount, _nearestPoints); for (const attribName of attribNames) { this._interpolatePoints(destPoint, _nearestPoints, attribName); } } private _interpolatePoints(pointDest: BaseCorePoint, srcPoints: BaseCorePoint[], attribName: string) { const newValue = CoreInterpolate.perform( pointDest, srcPoints, attribName, this.pv.distanceThreshold, this.pv.blendWidth ); if (newValue != null) { pointDest.setAttribValue(attribName, newValue); } } }