UNPKG

@polygonjs/polygonjs

Version:

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

191 lines (167 loc) 6.16 kB
/** * Resample input lines * */ import {LineSegments} from 'three'; import {Float32BufferAttribute} from 'three'; import {BufferGeometry} from 'three'; import {CatmullRomCurve3} from 'three'; import {mergeGeometries} from 'three/examples/jsm/utils/BufferGeometryUtils'; import {TypedSopNode} from './_Base'; import {ObjectType} from '../../../core/geometry/Constant'; import {CoreGeometryUtilCurve} from '../../../core/geometry/util/Curve'; export enum METHOD { POINTS_COUNT = 'pointsCount', SEGMENT_LENGTH = 'segmentLength', } export const METHODS = [METHOD.POINTS_COUNT, METHOD.SEGMENT_LENGTH]; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; import {CoreGroup} from '../../../core/geometry/Group'; import {BaseCorePoint, CorePoint} from '../../../core/geometry/entities/point/CorePoint'; import {TypeAssert} from '../../poly/Assert'; import {Vector3} from 'three'; import {SplineCurveType, SPLINE_CURVE_TYPES} from '../../../core/geometry/Curve'; import {InputCloneMode} from '../../poly/InputCloneMode'; import {pointsFromObject} from '../../../core/geometry/entities/point/CorePointUtils'; import {Attribute} from '../../../core/geometry/Attribute'; import {CoreObjectType} from '../../../core/geometry/ObjectContent'; const _points: CorePoint<CoreObjectType>[] = []; class ResampleSopParamsConfig extends NodeParamsConfig { /** @param resampling method */ method = ParamConfig.INTEGER(METHODS.indexOf(METHOD.POINTS_COUNT), { menu: { entries: METHODS.map((name, i) => { return { name: name, value: i, }; }), }, }); /** @param type of curve this will generate */ curveType = ParamConfig.INTEGER(SPLINE_CURVE_TYPES.indexOf(SplineCurveType.CATMULLROM), { range: [0, 2], rangeLocked: [true, true], menu: { entries: SPLINE_CURVE_TYPES.map((name, i) => { return { name: name, value: i, }; }), }, }); /** @param curve tension */ tension = ParamConfig.FLOAT(0.01, { range: [0, 1], rangeLocked: [true, true], visibleIf: {curveType: SPLINE_CURVE_TYPES.indexOf(SplineCurveType.CATMULLROM)}, }); /** @param points count */ pointsCount = ParamConfig.INTEGER(100, { visibleIf: {method: METHODS.indexOf(METHOD.POINTS_COUNT)}, range: [1, 1000], rangeLocked: [true, false], }); /** @param segments length */ segmentLength = ParamConfig.FLOAT(1, { visibleIf: {method: METHODS.indexOf(METHOD.SEGMENT_LENGTH)}, }); } const ParamsConfig = new ResampleSopParamsConfig(); export class ResampleSopNode extends TypedSopNode<ResampleSopParamsConfig> { override paramsConfig = ParamsConfig; static override type() { return 'resample'; } override initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.NEVER); } override cook(inputCoreGroups: CoreGroup[]) { const coreGroup = inputCoreGroups[0]; const resampledObjects: LineSegments[] = []; if (this.pv.pointsCount >= 2) { const coreObjects = coreGroup.threejsCoreObjects(); for (const coreObject of coreObjects) { const object = coreObject.object(); if ((object as LineSegments).isLineSegments) { const resampledObject = this._resample(object as LineSegments); if (resampledObject) { resampledObjects.push(resampledObject); } } } } this.setObjects(resampledObjects); } setCurveType(curveType: SplineCurveType) { this.p.curveType.set(SPLINE_CURVE_TYPES.indexOf(curveType)); } _resample(lineSegment: LineSegments) { const geometry = lineSegment.geometry as BufferGeometry; pointsFromObject(lineSegment, _points); const indices = geometry.getIndex()?.array; if (!indices) { return; } const accumulated_curve_point_indices = CoreGeometryUtilCurve.accumulatedCurvePointIndices(indices); // accumulated_curve_point_indices = [accumulated_curve_point_indices[0]] const geometries: BufferGeometry[] = []; for (let i = 0; i < accumulated_curve_point_indices.length; i++) { const curve_point_indices = accumulated_curve_point_indices[i]; const current_points = curve_point_indices.map((index) => _points[index]); const geometry = this._create_curve_from_points(current_points); if (geometry) { geometries.push(geometry); } } const mergedGeometry = mergeGeometries(geometries); const object = this.createObject(mergedGeometry, ObjectType.LINE_SEGMENTS); return object; } _create_curve_from_points(points: BaseCorePoint[]) { if (points.length <= 1) { return; } const old_curve_positions = points.map((point) => point.attribValue(Attribute.POSITION)) as Vector3[]; const closed = false; const curveType = SPLINE_CURVE_TYPES[this.pv.curveType]; const tension = this.pv.tension; const curve = new CatmullRomCurve3(old_curve_positions, closed, curveType, tension); // const curve = new LineCurve3(old_curve_positions); // const curve = new CubicBezierCurve3(old_curve_positions); // const curve = new QuadraticBezierCurve3(old_curve_positions); // TODO: could I detect when a curve has points that are very close // and prevent a curve to go too far const new_curve_points = this._get_points_from_curve(curve); let positions: number[][] = []; const indices: number[] = []; for (let i = 0; i < new_curve_points.length; i++) { const point_position = new_curve_points[i]; const position = point_position.toArray(); positions.push(position); if (i > 0) { indices.push(i - 1); indices.push(i); } } const geometry = new BufferGeometry(); geometry.setAttribute('position', new Float32BufferAttribute(positions.flat(), 3)); geometry.setIndex(indices); return geometry; } _get_points_from_curve(curve: CatmullRomCurve3) { const method = METHODS[this.pv.method]; switch (method) { case METHOD.POINTS_COUNT: return curve.getSpacedPoints(Math.max(2, this.pv.pointsCount)); case METHOD.SEGMENT_LENGTH: var length = curve.getLength(); var pointsCount = this.pv.segmentLength !== 0 ? 1 + length / this.pv.segmentLength : 2; pointsCount = Math.max(2, pointsCount); return curve.getSpacedPoints(pointsCount); } TypeAssert.unreachable(method); } }