ply-js
Version:
A TypeScript port based on python-plyfile for reading and writing .ply files
97 lines (96 loc) • 4.17 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.calibrateSamples = calibrateSamples;
exports.saveCalibrationFile = saveCalibrationFile;
exports.loadCalibrationFile = loadCalibrationFile;
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
// Compute calibrations (scale and density) per machine given labeled samples.
// `lib` should be the library exports (so we can call computeHeight/computeVolumeFromFaces).
async function calibrateSamples(samples, lib) {
const byMachine = new Map();
for (const s of samples) {
const file = path.resolve(s.file);
if (!fs.existsSync(file))
continue;
const ply = await lib.PlyData.read(file);
const vertex = ply.elements.find((e) => e.name === 'vertex');
const face = ply.elements.find((e) => e.name === 'face');
const pts = vertex.data.map((r) => Array.isArray(r) ? r.slice(0, 3) : [r.x ?? r[0], r.y ?? r[1], r.z ?? r[2]]).filter((p) => p.every((n) => typeof n === 'number'));
const faces = face ? face.data : null;
// measured height and geometric volume
const measuredH = lib.computeHeight(pts);
const geomVol = (faces && faces.length) ? lib.computeVolumeFromFaces(pts, faces) : lib.computeVolumeVoxel(pts, faces, 0.01, 500000);
if (!measuredH || !geomVol)
continue;
const scale = s.height / measuredH;
const scaledVol = geomVol * Math.pow(scale, 3);
const density = s.mass / scaledVol;
const machine = s.machine || 'default';
let rec = byMachine.get(machine);
if (!rec) {
rec = { scales: [], densities: [] };
byMachine.set(machine, rec);
}
rec.scales.push(scale);
rec.densities.push(density);
}
const out = {};
for (const [m, rec] of byMachine.entries()) {
const avgScale = rec.scales.reduce((a, b) => a + b, 0) / rec.scales.length;
const avgDensity = rec.densities.reduce((a, b) => a + b, 0) / rec.densities.length;
out[m] = { scale: avgScale, densityKgPerM3: avgDensity, count: rec.scales.length };
}
return out;
}
function saveCalibrationFile(id, data, folder = path.resolve(process.cwd(), 'calibrations')) {
if (!fs.existsSync(folder))
fs.mkdirSync(folder, { recursive: true });
const file = path.join(folder, `${id}.json`);
fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8');
return file;
}
function loadCalibrationFile(id, folder = path.resolve(process.cwd(), 'calibrations')) {
const file = path.join(folder, `${id}.json`);
if (!fs.existsSync(file))
return null;
try {
return JSON.parse(fs.readFileSync(file, 'utf8'));
}
catch (e) {
return null;
}
}
;