trimesh's 3MF loader re-reads a whole production-extension part file for every component that points into it. A Bambu Studio project with 884 components across four ~50 MB part files grew past 3 GB within seconds and failed both the preview conversion and the geometry fingerprint. - Add tools/threemf_stream.py: parses each part once with lxml iterparse, resolves each component to the single object it names, and returns each mesh once plus its placements. The same project now converts in ~8 s at ~380 MB peak (fingerprinting ~190 MB). - convert_3mf.py and fingerprint.py use it; the GLB stores each object once and instances repeats. Stats are computed per placement (mirrored placements keep a positive volume), without concatenating meshes. - Run sidecar scripts through one queue (lib/python.js) so a preview and background fingerprinting never load big meshes at the same time; previews go first, and 3MFs are fingerprinted one per run. - Surface the converter's JSON error / timeout instead of "Command failed". - Tests with a Bambu-style component fixture (shared part, mirrored placement, decoy object) and STL/3MF fingerprint parity. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
22 lines
941 B
JavaScript
22 lines
941 B
JavaScript
// Converts 3MF files to GLB via the Python/trimesh sidecar, so the browser can
|
|
// render Bambu/MakerWorld production-extension 3MFs (which the JS 3MFLoader can't).
|
|
|
|
const { runScript } = require('./python');
|
|
|
|
// Returns { glb, volume_cm3, bbox, faces } or throws.
|
|
async function convert3mfToGlb(inputPath, outputPath) {
|
|
const { err, stdout, stderr } = await runScript('convert_3mf.py', [inputPath, outputPath], { timeout: 120000 });
|
|
let result = null;
|
|
try {
|
|
result = JSON.parse(stdout.trim().split('\n').pop());
|
|
} catch { /* handled below */ }
|
|
if (result && result.error) throw new Error(result.error);
|
|
if (err) {
|
|
if (err.killed) throw new Error('Timed out converting this 3MF for preview.');
|
|
throw new Error(stderr ? stderr.trim().split('\n').slice(-3).join(' ') : err.message);
|
|
}
|
|
if (!result) throw new Error('Converter returned unparseable output.');
|
|
return result;
|
|
}
|
|
|
|
module.exports = { convert3mfToGlb };
|