ply-js
Version:
A TypeScript port based on python-plyfile for reading and writing .ply files
240 lines (239 loc) • 8.71 kB
JavaScript
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);
}
;