@loaders.gl/pcd
Version:
Framework-independent loader for the PCD format
413 lines (405 loc) • 13.9 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
PCDLoader: () => PCDLoader2,
PCDWorkerLoader: () => PCDLoader
});
module.exports = __toCommonJS(dist_exports);
// dist/lib/parse-pcd.js
var import_schema = require("@loaders.gl/schema");
// dist/lib/decompress-lzf.js
function decompressLZF(inData, outLength) {
const inLength = inData.length;
const outData = new Uint8Array(outLength);
let inPtr = 0;
let outPtr = 0;
let ctrl;
let len;
let ref;
do {
ctrl = inData[inPtr++];
if (ctrl < 1 << 5) {
ctrl++;
if (outPtr + ctrl > outLength) {
throw new Error("Output buffer is not large enough");
}
if (inPtr + ctrl > inLength) {
throw new Error("Invalid compressed data");
}
do {
outData[outPtr++] = inData[inPtr++];
} while (--ctrl);
} else {
len = ctrl >> 5;
ref = outPtr - ((ctrl & 31) << 8) - 1;
if (inPtr >= inLength) {
throw new Error("Invalid compressed data");
}
if (len === 7) {
len += inData[inPtr++];
if (inPtr >= inLength) {
throw new Error("Invalid compressed data");
}
}
ref -= inData[inPtr++];
if (outPtr + len + 2 > outLength) {
throw new Error("Output buffer is not large enough");
}
if (ref < 0) {
throw new Error("Invalid compressed data");
}
if (ref >= outPtr) {
throw new Error("Invalid compressed data");
}
do {
outData[outPtr++] = outData[ref++];
} while (--len + 2);
}
} while (inPtr < inLength);
return outData;
}
// dist/lib/get-pcd-schema.js
function getPCDSchema(PCDheader, metadata) {
const offset = PCDheader.offset;
const fields = [];
if (offset.x !== void 0) {
fields.push({
name: "POSITION",
type: { type: "fixed-size-list", listSize: 3, children: [{ name: "xyz", type: "float32" }] }
});
}
if (offset.normal_x !== void 0) {
fields.push({
name: "NORMAL",
type: { type: "fixed-size-list", listSize: 3, children: [{ name: "xyz", type: "float32" }] }
});
}
if (offset.rgb !== void 0) {
fields.push({
name: "COLOR_0",
type: { type: "fixed-size-list", listSize: 3, children: [{ name: "rgb", type: "uint8" }] }
});
}
return { fields, metadata };
}
// dist/lib/parse-pcd.js
var LITTLE_ENDIAN = true;
function parsePCD(data) {
const textData = new TextDecoder().decode(data);
const pcdHeader = parsePCDHeader(textData);
let attributes = {};
switch (pcdHeader.data) {
case "ascii":
attributes = parsePCDASCII(pcdHeader, textData);
break;
case "binary":
attributes = parsePCDBinary(pcdHeader, data);
break;
case "binary_compressed":
attributes = parsePCDBinaryCompressed(pcdHeader, data);
break;
default:
throw new Error(`PCD: ${pcdHeader.data} files are not supported`);
}
attributes = getMeshAttributes(attributes);
const header = getMeshHeader(pcdHeader, attributes);
const metadata = Object.fromEntries([
["mode", "0"],
["boundingBox", JSON.stringify(header.boundingBox)]
]);
const schema = getPCDSchema(pcdHeader, metadata);
return {
loader: "pcd",
loaderData: pcdHeader,
header,
schema,
mode: 0,
// POINTS
topology: "point-list",
attributes
};
}
function getMeshHeader(pcdHeader, attributes) {
if (typeof pcdHeader.width === "number" && typeof pcdHeader.height === "number") {
const pointCount = pcdHeader.width * pcdHeader.height;
return {
vertexCount: pointCount,
boundingBox: (0, import_schema.getMeshBoundingBox)(attributes)
};
}
return {
vertexCount: pcdHeader.vertexCount,
boundingBox: pcdHeader.boundingBox
};
}
function getMeshAttributes(attributes) {
const normalizedAttributes = {
POSITION: {
// Binary PCD is only 32 bit
value: new Float32Array(attributes.position),
size: 3
}
};
if (attributes.normal && attributes.normal.length > 0) {
normalizedAttributes.NORMAL = {
value: new Float32Array(attributes.normal),
size: 3
};
}
if (attributes.color && attributes.color.length > 0) {
normalizedAttributes.COLOR_0 = {
value: new Uint8Array(attributes.color),
size: 3
};
}
if (attributes.intensity && attributes.intensity.length > 0) {
normalizedAttributes.COLOR_0 = {
value: new Uint8Array(attributes.color),
size: 3
};
}
if (attributes.label && attributes.label.length > 0) {
normalizedAttributes.COLOR_0 = {
value: new Uint8Array(attributes.label),
size: 3
};
}
return normalizedAttributes;
}
function parsePCDHeader(data) {
const result1 = data.search(/[\r\n]DATA\s(\S*)\s/i);
const result2 = /[\r\n]DATA\s(\S*)\s/i.exec(data.substr(result1 - 1));
const pcdHeader = {};
pcdHeader.data = result2 && result2[1];
if (result2 !== null) {
pcdHeader.headerLen = (result2 && result2[0].length) + result1;
}
pcdHeader.str = data.substr(0, pcdHeader.headerLen);
pcdHeader.str = pcdHeader.str.replace(/\#.*/gi, "");
pcdHeader.version = /VERSION (.*)/i.exec(pcdHeader.str);
pcdHeader.fields = /FIELDS (.*)/i.exec(pcdHeader.str);
pcdHeader.size = /SIZE (.*)/i.exec(pcdHeader.str);
pcdHeader.type = /TYPE (.*)/i.exec(pcdHeader.str);
pcdHeader.count = /COUNT (.*)/i.exec(pcdHeader.str);
pcdHeader.width = /WIDTH (.*)/i.exec(pcdHeader.str);
pcdHeader.height = /HEIGHT (.*)/i.exec(pcdHeader.str);
pcdHeader.viewpoint = /VIEWPOINT (.*)/i.exec(pcdHeader.str);
pcdHeader.points = /POINTS (.*)/i.exec(pcdHeader.str);
if (pcdHeader.version !== null) {
pcdHeader.version = parseFloat(pcdHeader.version[1]);
}
if (pcdHeader.fields !== null) {
pcdHeader.fields = pcdHeader.fields[1].split(" ");
}
if (pcdHeader.type !== null) {
pcdHeader.type = pcdHeader.type[1].split(" ");
}
if (pcdHeader.width !== null) {
pcdHeader.width = parseInt(pcdHeader.width[1], 10);
}
if (pcdHeader.height !== null) {
pcdHeader.height = parseInt(pcdHeader.height[1], 10);
}
if (pcdHeader.viewpoint !== null) {
pcdHeader.viewpoint = pcdHeader.viewpoint[1];
}
if (pcdHeader.points !== null) {
pcdHeader.points = parseInt(pcdHeader.points[1], 10);
}
if (pcdHeader.points === null && typeof pcdHeader.width === "number" && typeof pcdHeader.height === "number") {
pcdHeader.points = pcdHeader.width * pcdHeader.height;
}
if (pcdHeader.size !== null) {
pcdHeader.size = pcdHeader.size[1].split(" ").map((x) => parseInt(x, 10));
}
if (pcdHeader.count !== null) {
pcdHeader.count = pcdHeader.count[1].split(" ").map((x) => parseInt(x, 10));
} else {
pcdHeader.count = [];
if (pcdHeader.fields !== null) {
for (let i = 0; i < pcdHeader.fields.length; i++) {
pcdHeader.count.push(1);
}
}
}
pcdHeader.offset = {};
let sizeSum = 0;
if (pcdHeader.fields !== null && pcdHeader.size !== null) {
for (let i = 0; i < pcdHeader.fields.length; i++) {
if (pcdHeader.data === "ascii") {
pcdHeader.offset[pcdHeader.fields[i]] = i;
} else {
pcdHeader.offset[pcdHeader.fields[i]] = sizeSum;
sizeSum += pcdHeader.size[i];
}
}
}
pcdHeader.rowSize = sizeSum;
return pcdHeader;
}
function parsePCDASCII(pcdHeader, textData) {
const position = [];
const normal = [];
const color = [];
const intensity = [];
const label = [];
const offset = pcdHeader.offset;
const pcdData = textData.substr(pcdHeader.headerLen);
const lines = pcdData.split("\n");
for (let i = 0; i < lines.length; i++) {
if (lines[i] !== "") {
const line = lines[i].split(" ");
if (offset.x !== void 0) {
position.push(parseFloat(line[offset.x]));
position.push(parseFloat(line[offset.y]));
position.push(parseFloat(line[offset.z]));
}
if (offset.rgb !== void 0) {
const floatValue = parseFloat(line[offset.rgb]);
const binaryColor = new Float32Array([floatValue]);
const dataview = new DataView(binaryColor.buffer, 0);
color.push(dataview.getUint8(0));
color.push(dataview.getUint8(1));
color.push(dataview.getUint8(2));
}
if (offset.normal_x !== void 0) {
normal.push(parseFloat(line[offset.normal_x]));
normal.push(parseFloat(line[offset.normal_y]));
normal.push(parseFloat(line[offset.normal_z]));
}
if (offset.intensity !== void 0) {
intensity.push(parseFloat(line[offset.intensity]));
}
if (offset.label !== void 0) {
label.push(parseInt(line[offset.label]));
}
}
}
return { position, normal, color };
}
function parsePCDBinary(pcdHeader, data) {
const position = [];
const normal = [];
const color = [];
const intensity = [];
const label = [];
const dataview = new DataView(data, pcdHeader.headerLen);
const offset = pcdHeader.offset;
for (let i = 0, row = 0; i < pcdHeader.points; i++, row += pcdHeader.rowSize) {
if (offset.x !== void 0) {
position.push(dataview.getFloat32(row + offset.x, LITTLE_ENDIAN));
position.push(dataview.getFloat32(row + offset.y, LITTLE_ENDIAN));
position.push(dataview.getFloat32(row + offset.z, LITTLE_ENDIAN));
}
if (offset.rgb !== void 0) {
color.push(dataview.getUint8(row + offset.rgb + 0));
color.push(dataview.getUint8(row + offset.rgb + 1));
color.push(dataview.getUint8(row + offset.rgb + 2));
}
if (offset.normal_x !== void 0) {
normal.push(dataview.getFloat32(row + offset.normal_x, LITTLE_ENDIAN));
normal.push(dataview.getFloat32(row + offset.normal_y, LITTLE_ENDIAN));
normal.push(dataview.getFloat32(row + offset.normal_z, LITTLE_ENDIAN));
}
if (offset.intensity !== void 0) {
intensity.push(dataview.getFloat32(row + offset.intensity, LITTLE_ENDIAN));
}
if (offset.label !== void 0) {
label.push(dataview.getInt32(row + offset.label, LITTLE_ENDIAN));
}
}
return { position, normal, color, intensity, label };
}
function parsePCDBinaryCompressed(pcdHeader, data) {
const position = [];
const normal = [];
const color = [];
const intensity = [];
const label = [];
const sizes = new Uint32Array(data.slice(pcdHeader.headerLen, pcdHeader.headerLen + 8));
const compressedSize = sizes[0];
const decompressedSize = sizes[1];
const decompressed = decompressLZF(new Uint8Array(data, pcdHeader.headerLen + 8, compressedSize), decompressedSize);
const dataview = new DataView(decompressed.buffer);
const offset = pcdHeader.offset;
for (let i = 0; i < pcdHeader.points; i++) {
if (offset.x !== void 0) {
position.push(dataview.getFloat32(pcdHeader.points * offset.x + pcdHeader.size[0] * i, LITTLE_ENDIAN));
position.push(dataview.getFloat32(pcdHeader.points * offset.y + pcdHeader.size[1] * i, LITTLE_ENDIAN));
position.push(dataview.getFloat32(pcdHeader.points * offset.z + pcdHeader.size[2] * i, LITTLE_ENDIAN));
}
if (offset.rgb !== void 0) {
color.push(dataview.getUint8(pcdHeader.points * offset.rgb + pcdHeader.size[3] * i + 0) / 255);
color.push(dataview.getUint8(pcdHeader.points * offset.rgb + pcdHeader.size[3] * i + 1) / 255);
color.push(dataview.getUint8(pcdHeader.points * offset.rgb + pcdHeader.size[3] * i + 2) / 255);
}
if (offset.normal_x !== void 0) {
normal.push(dataview.getFloat32(pcdHeader.points * offset.normal_x + pcdHeader.size[4] * i, LITTLE_ENDIAN));
normal.push(dataview.getFloat32(pcdHeader.points * offset.normal_y + pcdHeader.size[5] * i, LITTLE_ENDIAN));
normal.push(dataview.getFloat32(pcdHeader.points * offset.normal_z + pcdHeader.size[6] * i, LITTLE_ENDIAN));
}
if (offset.intensity !== void 0) {
const intensityIndex = pcdHeader.fields.indexOf("intensity");
intensity.push(dataview.getFloat32(pcdHeader.points * offset.intensity + pcdHeader.size[intensityIndex] * i, LITTLE_ENDIAN));
}
if (offset.label !== void 0) {
const labelIndex = pcdHeader.fields.indexOf("label");
label.push(dataview.getInt32(pcdHeader.points * offset.label + pcdHeader.size[labelIndex] * i, LITTLE_ENDIAN));
}
}
return {
position,
normal,
color,
intensity,
label
};
}
// dist/pcd-loader.js
var VERSION = true ? "4.3.2" : "latest";
var PCDLoader = {
dataType: null,
batchType: null,
name: "PCD (Point Cloud Data)",
id: "pcd",
module: "pcd",
version: VERSION,
worker: true,
extensions: ["pcd"],
mimeTypes: ["text/plain"],
options: {
pcd: {}
}
};
// dist/index.js
var PCDLoader2 = {
...PCDLoader,
parse: async (arrayBuffer) => parsePCD(arrayBuffer),
parseSync: parsePCD
};
/** Parse compressed PCD data in in binary_compressed form ( https://pointclouds.org/documentation/tutorials/pcd_file_format.html)
* from https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/PCDLoader.js
* @license MIT (http://opensource.org/licenses/MIT)
* @param pcdHeader
* @param data
* @returns [attributes]
*/
//# sourceMappingURL=index.cjs.map