/**
* @typedef {Object} Voxel The actual voxel data, describing a filled voxel.
* @property {number} x The x coordinate of the voxel.
* @property {number} y The y coordinate of the voxel.
* @property {number} z The z coordinate of the voxel.
*/
/**
* @typedef {Object} VoxelData Parsed BINVOX file data structure representation.
* @property {Object} dimension The dimension of the voxel data.
* @property {number} dimension.depth The depth dimension of the voxel data.
* @property {number} dimension.width The width dimension of the voxel data.
* @property {number} dimension.height The height dimension of the voxel data.
* @property {Object} translate The translation of the voxel data.
* @property {number} translate.depth The depth translation of the voxel data.
* @property {number} translate.width The width translation of the voxel data.
* @property {number} translate.height The height translation of the voxel data.
* @property {number} scale The scaling of the voxel data.
* @property {Array<Voxel>} voxels The actual voxel data, describing filled voxels.
*/
/**
* Parser for parsing BINVOX voxel file data.
*/
export class Parser {
/**
* Creates a BINVOX Parser.
*/
constructor() {
this.dimension = {};
this.translation = {};
this.scale = 1;
this.voxels = [];
this.index = 0;
}
/**
* Parse BINVOX file buffer data.
* @param {ArrayBuffer} buffer BINVOX buffer data.
* @returns {VoxelData} The parsed voxel data.
*/
parse(buffer) {
this._parseHeader(buffer);
this._parseVoxelData(buffer);
return {
dimension: this.dimension,
translate: this.translation,
scale: this.scale,
voxels: this.voxels
}
}
/**
* Parse the BINVOX ASCII file header.
* @param {ArrayBuffer} buffer BINVOX file buffer data.
* @private
*/
_parseHeader(buffer) {
var decoder = new TextDecoder('ascii');
let continueReading = true;
let lines = [];
let i = this.index;
let line = "";
while (continueReading) {
let char = decoder.decode(buffer.slice(i, i + 1));
if (char === "\n") {
lines.push(line);
line = "";
} else {
line += char;
}
if (line === "Data" || lines.length >= 5) {
continueReading = false;
}
i++;
}
this.index = i;
let version = lines[0];
let dimension = lines[1];
let translate = lines[2];
let scale = lines[3];
let data = lines[4];
// Check "version" line
if (version !== "#binvox 1") {
throw new Error("First line reads \"" + version + "\" instead of \"#binvox\"");
}
// Parse "dimension"
let dimensionArray = dimension.split(" ");
if (dimensionArray[0] !== "dim") {
throw new Error("Error reading dimension line");
}
this.dimension = { depth: parseInt(dimensionArray[1]), width: parseInt(dimensionArray[2]), height: parseInt(dimensionArray[3]) };
// Parse "translation"
let translateArray = translate.split(" ");
if (translateArray[0] !== "translate") {
throw new Error("Error reading translate line");
}
this.translation = { depth: parseFloat(translateArray[1]), width: parseFloat(translateArray[2]), height: parseFloat(translateArray[3]) };
// Parse "scale"
let scaleArray = scale.split(" ");
if (scaleArray[0] !== "scale") {
throw new Error("Error reading scale line");
}
this.scale = parseFloat(scaleArray[1]);
// Check "data" line
if (data !== "data") {
throw new Error("Error reading header");
}
}
/**
* Parse the voxel buffer data.
* @param {ArrayBuffer} buffer BINVOX file voxel buffer data.
* @private
*/
_parseVoxelData(buffer) {
var int8view = new Uint8Array(buffer, this.index);
let i = 0;
let y = 0;
let z = 0;
let x = 0;
while (i < int8view.length) {
const value = int8view[i];
const count = int8view[i + 1];
for (let j = 0; j < count; j++) {
if (value === 1) {
let point = { x: x, y: y, z: z };
this.voxels.push(point);
}
y++;
if (y === this.dimension.width) {
y = 0;
z++;
}
if (z === this.dimension.height) {
z = 0;
x++;
}
}
i += 2;
}
}
}