UNPKG

ply-js

Version:

A TypeScript port based on python-plyfile for reading and writing .ply files

160 lines (159 loc) 6.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PlyElement = void 0; const property_1 = require("./property"); const errors_1 = require("./errors"); const utils_1 = require("./utils"); class PlyElement { constructor(name, properties, count, comments = []) { this._propertyLookup = new Map(); this._comments = []; this.data = []; this._name = String(name); this._properties = [...properties]; this._count = count; this.comments = comments; this._index(); this._haveList = this._properties.some(p => p instanceof property_1.PlyListProperty); } get name() { return this._name; } get count() { return this._count; } get properties() { return this._properties; } set dataArray(rows) { this.data = rows; this._count = rows.length; this._checkSanity(); } get comments() { return [...this._comments]; } set comments(c) { this._comments = [...c]; } _index() { this._propertyLookup = new Map(this._properties.map(p => [p.name, p])); if (this._propertyLookup.size !== this._properties.length) throw new Error('two properties with same name'); } _checkSanity() { for (const prop of this._properties) { for (const rec of this.data) { if (!(prop.name in rec)) throw new Error(`dangling property '${prop.name}'`); } } } plyProperty(name) { return this._propertyLookup.get(name); } dtype(byteOrder = '=') { // TS version: return tuple list; consumers can map as needed return this._properties.map(p => [p.name, p.dtype(byteOrder)]); } header() { const lines = [`element ${this.name} ${this.count}`]; for (const c of this._comments) lines.push(`comment ${c}`); for (const p of this._properties) lines.push(String(p)); return lines.join('\n'); } // When reading binary, return the number of bytes consumed from the buffer _read(stream, isText, byteOrder, _mmap, knownListLen = {}) { if (isText) return this._readTxt(stream.toString('utf8')); return this._readBin(stream, byteOrder); } _readTxt(text) { const lines = text.split(/\r?\n/).filter(Boolean).slice(0, this.count); if (lines.length < this.count) throw new errors_1.PlyElementParseError('early end-of-file', this, lines.length); this.data = new Array(this.count); for (let k = 0; k < this.count; k++) { const parts = lines[k].trim().split(/\s+/); const it = parts[Symbol.iterator](); const rec = {}; for (const prop of this._properties) { try { const v = prop._fromFields(it); rec[prop.name] = v; } catch (e) { if (e?.message === 'StopIteration') throw new errors_1.PlyElementParseError('early end-of-line', this, k, prop); if (e?.message === 'ValueError') throw new errors_1.PlyElementParseError('malformed input', this, k, prop); throw e; } } // Ensure no extra tokens if (!it.next().done) throw new errors_1.PlyElementParseError('expected end-of-line', this, k); this.data[k] = rec; } } _readBin(buffer, byteOrder) { const bo = (0, utils_1.normalizeByteOrder)(false, byteOrder); let offset = 0; this.data = new Array(this.count); for (let k = 0; k < this.count; k++) { const rec = {}; for (const prop of this._properties) { if (process.env.PLY_DEBUG === '1') { try { // eslint-disable-next-line no-console console.debug(`PlyElement._readBin: element=${this._name} row=${k} prop=${prop.name} offset=${offset} bufferLen=${buffer.length}`); } catch { /** ignore debug errors */ } } if (prop instanceof property_1.PlyListProperty) { try { const { value, next } = prop._readBin(buffer, offset, bo); offset = next; rec[prop.name] = value; } catch { // include more context when debugging if (process.env.PLY_DEBUG === '1') { // eslint-disable-next-line no-console console.debug(`PlyElement._readBin: early EOF while reading list prop=${prop.name} at offset=${offset} element=${this._name} row=${k}`); } throw new errors_1.PlyElementParseError('early end-of-file', this, k, prop); } } else { try { const { value, next } = prop._readBin(buffer, offset, bo); offset = next; rec[prop.name] = value; } catch { if (process.env.PLY_DEBUG === '1') { // eslint-disable-next-line no-console console.debug(`PlyElement._readBin: early EOF while reading scalar prop=${prop.name} at offset=${offset} element=${this._name} row=${k}`); } throw new errors_1.PlyElementParseError('early end-of-file', this, k, prop); } } } this.data[k] = rec; } // return number of bytes consumed from buffer return offset; } _write(streams, isText, byteOrder) { if (isText) return this._writeTxt(streams); return this._writeBin(streams, byteOrder); } _writeTxt(out) { for (const rec of this.data) { const fields = []; for (const prop of this._properties) { for (const v of prop._toFields(rec[prop.name])) fields.push(Number(v)); } out.push(fields.join(' ') + '\n'); } } _writeBin(out, byteOrder) { for (const rec of this.data) { for (const prop of this._properties) { const data = rec[prop.name]; const buf = prop._writeBin(data, byteOrder); out.push(buf); } } } } exports.PlyElement = PlyElement;