UNPKG

ply-js

Version:

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

240 lines (239 loc) 8.71 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.typesList = exports.dataTypeReverse = exports.dataTypes = exports.dataTypeRelation = exports.byteOrderReverse = exports.byteOrderMap = exports.nativeByteOrder = void 0; exports.lookupType = lookupType; exports.checkName = checkName; exports.checkComments = checkComments; exports.decodeAscii = decodeAscii; exports.readArray = readArray; exports.writeArray = writeArray; exports.normalizeByteOrder = normalizeByteOrder; exports.expect = expect; /* * This file is part of python-plyfile (original work Copyright © 2014-2025 Darsh Ranjan * and plyfile authors). TypeScript port © 2025 Gustavo Diogo Silva (GitHub: GustavoDiogo). * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this * program. If not, see <http://www.gnu.org/licenses/>. */ const os_1 = __importDefault(require("os")); const errors_1 = require("./errors"); exports.nativeByteOrder = os_1.default.endianness() === 'LE' ? '<' : '>'; exports.byteOrderMap = { ascii: '=', binary_little_endian: '<', binary_big_endian: '>', }; exports.byteOrderReverse = { '<': 'binary_little_endian', '>': 'binary_big_endian', '=': 'ascii', // only used when formatting text header }; // Many-to-many mapping preserved from original exports.dataTypeRelation = [ ['int8', 'i1'], ['char', 'i1'], ['uint8', 'u1'], ['uchar', 'b1'], ['uchar', 'u1'], ['int16', 'i2'], ['short', 'i2'], ['uint16', 'u2'], ['ushort', 'u2'], ['int32', 'i4'], ['int', 'i4'], ['uint32', 'u4'], ['uint', 'u4'], ['float32', 'f4'], ['float', 'f4'], ['float64', 'f8'], ['double', 'f8'], ]; exports.dataTypes = Object.fromEntries(exports.dataTypeRelation); exports.dataTypeReverse = Object.fromEntries(exports.dataTypeRelation.map(([a, b]) => [b, a])); exports.typesList = (() => { const set = new Set(); const list = []; for (const [a, b] of exports.dataTypeRelation) { if (!set.has(a)) { list.push(a); set.add(a); } if (!set.has(b)) { list.push(b); set.add(b); } } return list; })(); function lookupType(typeStr) { // If the user provided a long name (e.g. 'float'), map to the short canonical code ('f4'). if (typeStr in exports.dataTypes) return exports.dataTypes[typeStr]; // If the user already provided a short code ('f4','i4', etc), accept it as-is. if (typeStr in exports.dataTypeReverse) return typeStr; throw new Error(`field type '${typeStr}' not in ${JSON.stringify(exports.typesList)}`); } function checkName(name) { for (const ch of name) { const code = ch.charCodeAt(0); if (!(0 <= code && code < 128)) throw new Error(`non-ASCII character in name '${name}'`); if (/\s/.test(ch)) throw new Error(`space character(s) in name '${name}'`); } } function checkComments(comments) { for (const c of comments) { for (const ch of c) { const code = ch.charCodeAt(0); if (!(0 <= code && code < 128)) throw new Error('non-ASCII character in comment'); if (ch === '\n') throw new Error('embedded newline in comment'); } } } function decodeAscii(bufOrStr) { return typeof bufOrStr === 'string' ? bufOrStr : bufOrStr.toString('ascii'); } function readArray(view, offset, count, type, order) { // Use Buffer read helpers to avoid DataView/ArrayBuffer offset pitfalls. // accept both short codes ('f4') and long names ('float') if (!(type in exports.dataTypeReverse) && (type in exports.dataTypes)) { type = exports.dataTypes[type]; } const little = order === '<' || (order === '=' && exports.nativeByteOrder === '<'); if (process.env.PLY_DEBUG === '1') { // eslint-disable-next-line no-console console.error(`readArray: type=${type} offset=${offset} count=${count} bufLen=${view.length} order=${order}`); } const out = []; const sizes = { i1: 1, u1: 1, i2: 2, u2: 2, i4: 4, u4: 4, f4: 4, f8: 8 }; let pos = offset; for (let i = 0; i < count; i++) { switch (type) { case 'i1': if (pos + 1 > view.length) throw new Error('StopIteration'); out.push(view.readInt8(pos)); pos += 1; break; case 'u1': if (pos + 1 > view.length) throw new Error('StopIteration'); out.push(view.readUInt8(pos)); pos += 1; break; case 'i2': if (pos + 2 > view.length) throw new Error('StopIteration'); out.push(little ? view.readInt16LE(pos) : view.readInt16BE(pos)); pos += 2; break; case 'u2': if (pos + 2 > view.length) throw new Error('StopIteration'); out.push(little ? view.readUInt16LE(pos) : view.readUInt16BE(pos)); pos += 2; break; case 'i4': if (pos + 4 > view.length) throw new Error('StopIteration'); out.push(little ? view.readInt32LE(pos) : view.readInt32BE(pos)); pos += 4; break; case 'u4': if (pos + 4 > view.length) throw new Error('StopIteration'); out.push(little ? view.readUInt32LE(pos) : view.readUInt32BE(pos)); pos += 4; break; case 'f4': if (pos + 4 > view.length) throw new Error('StopIteration'); out.push(little ? view.readFloatLE(pos) : view.readFloatBE(pos)); pos += 4; break; case 'f8': if (pos + 8 > view.length) { if (process.env.PLY_DEBUG === '1') console.error(`readArray: early EOF when reading f8 at pos=${pos} bufLen=${view.length}`); throw new Error('StopIteration'); } out.push(little ? view.readDoubleLE(pos) : view.readDoubleBE(pos)); pos += 8; break; default: throw new Error(`unsupported dtype '${type}'`); } } return { values: out, next: pos }; } function writeArray(values, type, order) { const sizes = { i1: 1, u1: 1, i2: 2, u2: 2, i4: 4, u4: 4, f4: 4, f8: 8 }; const size = values.length * sizes[type]; const buf = Buffer.allocUnsafe(size); const dv = new DataView(buf.buffer, buf.byteOffset, size); const little = order === '<' || (order === '=' && exports.nativeByteOrder === '<'); let pos = 0; const put = (code, v) => { switch (code) { case 'i1': dv.setInt8(pos, v); break; case 'u1': dv.setUint8(pos, v); break; case 'i2': dv.setInt16(pos, v, little); break; case 'u2': dv.setUint16(pos, v, little); break; case 'i4': dv.setInt32(pos, v, little); break; case 'u4': dv.setUint32(pos, v, little); break; case 'f4': dv.setFloat32(pos, v, little); break; case 'f8': dv.setFloat64(pos, v, little); break; default: throw new Error(`unsupported dtype '${code}'`); } pos += sizes[code]; }; for (const v of values) put(type, v); return buf; } function normalizeByteOrder(text, bo) { if (!text && bo === '=') return exports.nativeByteOrder; return bo; } function expect(cond, message, line) { if (!cond) throw new errors_1.PlyHeaderParseError(message, line); }