import {BaseSopOperation} from './_Base'; import {CoreGroup} from '../../geometry/Group'; import {DefaultOperationParams} from '../_Base'; import {BufferGeometry} from 'three/src/core/BufferGeometry'; import {BufferAttribute} from 'three/src/core/BufferAttribute'; import {ObjectType} from '../../../core/geometry/Constant'; import {InputCloneMode} from '../../../engine/poly/InputCloneMode'; import {CoreIterator} from '../../Iterator'; import {CoreMath} from '../../math/_Module'; import {CoreType} from '../../Type'; import {ArrayUtils} from '../../ArrayUtils'; interface ScatterSopParams extends DefaultOperationParams { pointsCount: number; seed: number; transferAttributes: boolean; attributesToTransfer: string; addIdAttribute: boolean; addIdnAttribute: boolean; } export class ScatterSopOperation extends BaseSopOperation { static readonly DEFAULT_PARAMS: ScatterSopParams = { pointsCount: 100, seed: 0, transferAttributes: true, attributesToTransfer: 'normal', addIdAttribute: true, addIdnAttribute: true, }; static readonly INPUT_CLONED_STATE = InputCloneMode.FROM_NODE; static type(): Readonly<'scatter'> { return 'scatter'; } async cook(input_contents: CoreGroup[], params: ScatterSopParams) { const core_group = input_contents[0]; let faces = core_group.faces(); const areas_thresholds: number[] = []; let area_sum = 0; const area_by_face_index: Map = new Map(); for (let face of faces) { const area = face.area(); area_by_face_index.set(face.index(), area); } const sorted_faces = ArrayUtils.sortBy(faces, (f) => { return area_by_face_index.get(f.index()) || -1; }); let i = 0; for (let face of sorted_faces) { area_sum += area_by_face_index.get(face.index()) as number; areas_thresholds[i] = area_sum; i++; } const positions: number[] = []; let attrib_names: string[] = []; if (params.transferAttributes) { attrib_names = core_group.attribNamesMatchingMask(params.attributesToTransfer); } const attrib_values_by_name: Map = new Map(); const attrib_sizes_by_name: Map = new Map(); for (let attrib_name of attrib_names) { attrib_values_by_name.set(attrib_name, []); attrib_sizes_by_name.set(attrib_name, core_group.attribSize(attrib_name)); } const iterator = new CoreIterator(); // await iterator.start_with_count(params.pointsCount, this._add_point.bind(this)) await iterator.start_with_count(params.pointsCount, (point_index: number) => { const rand = CoreMath.rand_float(params.seed + point_index) * area_sum; for (let face_index = 0; face_index < areas_thresholds.length; face_index++) { const areas_threshold = areas_thresholds[face_index]; if (rand <= areas_threshold) { const face = sorted_faces[face_index]; const position = face.random_position(rand); position.toArray(positions, positions.length); for (let attrib_name of attrib_names) { const attrib_value = face.attrib_value_at_position(attrib_name, position); if (attrib_value) { if (CoreType.isNumber(attrib_value)) { attrib_values_by_name.get(attrib_name)!.push(attrib_value); } else { attrib_value.toArray( attrib_values_by_name.get(attrib_name), attrib_values_by_name.get(attrib_name)!.length ); } } } break; } } }); // for(let point_index=0; point_index id / (pointsCount - 1)); if (params.addIdnAttribute) { geometry.setAttribute('idn', new BufferAttribute(new Float32Array(idns), 1)); } } const object = this.create_object(geometry, ObjectType.POINTS); return this.create_core_group_from_objects([object]); } }