UNPKG

polygonjs-engine

Version:

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

149 lines (137 loc) 5.56 kB
import {Float32BufferAttribute} from 'three/src/core/BufferAttribute'; import {BufferGeometry} from 'three/src/core/BufferGeometry'; import {CoreAttributeData} from '../../geometry/AttributeData'; import {CoreAttribute} from '../../geometry/Attribute'; import {AttribType} from '../../geometry/Constant'; import {CoreGeometry} from '../../geometry/Geometry'; import {CoreType} from '../../Type'; import {PolyDictionary} from '../../../types/GlobalTypes'; type CsvValue = string | number | number[]; const POSITION = 'position'; export class CsvLoader { static SEPARATOR = ','; static VECTOR_SEPARATOR = ','; private attribute_names_from_first_line: boolean = false; private lines: string[] = []; private points_count: number = 0; private attribute_values_by_name: PolyDictionary<CsvValue[]> = {}; // const attribute_types_by_name: PolyDictionary<AttribType> = {}; // const attribute_sizes_by_name: PolyDictionary<1|2|3|4> = {}; private attribute_data_by_name: PolyDictionary<CoreAttributeData> = {}; private _loading = false; constructor(private attribute_names?: string[]) { if (!this.attribute_names) { this.attribute_names_from_first_line = true; } } async load(url: string) { // we need to check if it is currently loading, as we accumulate the points_count durig read. // If another load was to start before the first load is completed, the points_count would be messed up. if (this._loading) { console.warn('is already loading'); return; } this._loading = true; this.points_count = 0; await this.load_data(url); this.infer_types(); this.read_values(); const geometry = this.create_points(); return geometry; } private async load_data(url: string) { const response = await fetch(url); const text = await response.text(); this.lines = text.split('\n'); if (!this.attribute_names) { this.attribute_names = this.lines[0].split(CsvLoader.SEPARATOR); } this.attribute_names = this.attribute_names.map((name) => CoreAttribute.remap_name(name)); for (let attribute_name of this.attribute_names) { this.attribute_values_by_name[attribute_name] = []; } } private infer_types() { const first_values_index = this.attribute_names_from_first_line ? 1 : 0; const first_line = this.lines[first_values_index]; let line_attribute_values = first_line.split(CsvLoader.SEPARATOR); for (let i = 0; i < line_attribute_values.length; i++) { const attribute_name = this.attribute_names![i]; const attribute_value = line_attribute_values[i]; const value = this._value_from_line_element(attribute_value); this.attribute_data_by_name[attribute_name] = CoreAttributeData.from_value(value); } } private _value_from_line_element(attribute_value: number | string) { if (CoreType.isString(attribute_value)) { if (`${parseFloat(attribute_value)}` === attribute_value) { return parseFloat(attribute_value); } else if (attribute_value[0] === '[' && attribute_value[attribute_value.length - 1] === ']') { const attribute_value_within_brackets = attribute_value.substring(1, attribute_value.length - 1); const elements_s = attribute_value_within_brackets.split(CsvLoader.VECTOR_SEPARATOR); return elements_s.map((element_s) => parseFloat(element_s)); } else { return attribute_value; } } else { return attribute_value; } } read_values() { if (!this.attribute_names) { return; } const first_values_index = this.attribute_names_from_first_line ? 1 : 0; let line: string; for (let line_index = first_values_index; line_index < this.lines.length; line_index++) { line = this.lines[line_index]; const line_attribute_values = line.split(CsvLoader.SEPARATOR); if (line_attribute_values.length >= this.attribute_names.length) { // ensure we are not on an empty line for (let i = 0; i < line_attribute_values.length; i++) { const attribute_name = this.attribute_names[i]; if (attribute_name) { const attribute_value = line_attribute_values[i]; const value = this._value_from_line_element(attribute_value); this.attribute_values_by_name[attribute_name].push(value); } } this.points_count += 1; } } // create position if not present in data if (!this.attribute_values_by_name[POSITION]) { const positions: number[] = new Array(this.points_count * 3); positions.fill(0); this.attribute_values_by_name[POSITION] = positions; this.attribute_data_by_name[POSITION] = new CoreAttributeData(3, AttribType.NUMERIC); this.attribute_names.push(POSITION); } } create_points() { if (!this.attribute_names) { return; } // create geometry const geometry = new BufferGeometry(); const core_geometry = new CoreGeometry(geometry); for (let attribute_name of this.attribute_names) { const attribute_values = this.attribute_values_by_name[attribute_name].flat(); const size = this.attribute_data_by_name[attribute_name].size(); const type = this.attribute_data_by_name[attribute_name].type(); if (type == AttribType.STRING) { const index_data = CoreAttribute.array_to_indexed_arrays(attribute_values as string[]); core_geometry.setIndexedAttribute(attribute_name, index_data['values'], index_data['indices']); } else { geometry.setAttribute(attribute_name, new Float32BufferAttribute(attribute_values as number[], size)); } } // add index const indices: number[] = new Array(this.points_count); for (let i = 0; i < this.points_count; i++) { indices.push(i); } geometry.setIndex(indices); return geometry; } }