@chestnutlabs/gcode-model-renderer 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"three-mf.d.ts","sourceRoot":"","sources":["../src/three-mf.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,KAAK,UAAU,EAEhB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAkC,KAAK,WAAW,EAAuB,MAAM,aAAa,CAAC;AAkbpG,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;CACnD;AA0FD,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,UAAU,GAAG,WAAW,EAChC,MAAM,CAAC,EAAE,WAAW,EACpB,IAAI,CAAC,EAAE,eAAe,GACrB,OAAO,CAAC,UAAU,CAAC,CAyKrB"}
1
+ {"version":3,"file":"three-mf.d.ts","sourceRoot":"","sources":["../src/three-mf.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,KAAK,UAAU,EAEhB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAkC,KAAK,WAAW,EAAuB,MAAM,aAAa,CAAC;AAkbpG,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;CACnD;AA4GD,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,UAAU,GAAG,WAAW,EAChC,MAAM,CAAC,EAAE,WAAW,EACpB,IAAI,CAAC,EAAE,eAAe,GACrB,OAAO,CAAC,UAAU,CAAC,CAyKrB"}
package/dist/three-mf.js CHANGED
@@ -428,17 +428,27 @@ const EST_REJECT_MARGIN = 2;
428
428
  /**
429
429
  * Fast structural cost estimate (DD-022 Phase 0), run after the (tiny, for production-extension files)
430
430
  * main part is parsed but BEFORE any large external geometry part is decompressed. Walks the build-item /
431
- * component tree counting instance placements and a lower-bound triangle estimate:
432
- * - a mesh object's own triangles are counted exactly (already parsed);
433
- * - an external `p:path` component is estimated from its ZIP-directory uncompressed size / the
434
- * conservative bytes-per-triangle bound, and treated as a leaf (not recursed) — a safe under-count.
435
- * Throws {@link ModelParseError} for a clear instance-bomb / oversize file so it rejects in sub-second
436
- * time instead of ~10 s of decompress-and-bake. Files near the budget do not reject here — the exact
437
- * per-triangle limit in the real parse still applies.
431
+ * component tree counting **placements** (all of them) and a lower-bound **unique**-triangle estimate:
432
+ * - a mesh master's triangles are counted **once per unique master** (already parsed), NOT per placement;
433
+ * - an external `p:path` part is estimated from its ZIP-directory uncompressed size / the conservative
434
+ * bytes-per-triangle bound, counted **once per unique path** (a safe under-count), treated as a leaf.
435
+ * Because instancing renders each master once (DD-022 Phase 1), the triangle estimate must be UNIQUE — a
436
+ * full-sheet plate of ~40 copies costs its ~1 master, not ~40× baked, so it is NOT rejected here. The
437
+ * placement count still guards an instance bomb (`maxInstances`). Throws {@link ModelParseError} for a
438
+ * clear instance-bomb / oversize file so it rejects in sub-second time instead of ~10 s of decompress-and-
439
+ * bake; files near the budget fall through to the exact per-triangle parse.
438
440
  */
439
441
  function estimateSceneCost(mainPart, entryByName, lim) {
440
442
  let instanceCount = 0;
441
443
  let estTriangles = 0;
444
+ // Unique masters already counted toward the triangle estimate (keyed by objectid / external path), so a
445
+ // reused master is counted once — matching the instanced render, not the old per-placement bake.
446
+ const countedMasters = new Set();
447
+ const checkInstances = () => {
448
+ if (instanceCount > lim.maxInstances) {
449
+ throw new ModelParseError('E_MODEL_TOO_MANY_INSTANCES', `3MF declares more than ${lim.maxInstances} instance placements`);
450
+ }
451
+ };
442
452
  const visit = (part, objectid, depth) => {
443
453
  if (depth > MAX_DEPTH)
444
454
  return;
@@ -447,23 +457,30 @@ function estimateSceneCost(mainPart, entryByName, lim) {
447
457
  return;
448
458
  if (obj.tris.length > 0) {
449
459
  instanceCount++;
450
- estTriangles += obj.tris.length / 3;
460
+ const key = `local:${objectid}`;
461
+ if (!countedMasters.has(key)) {
462
+ countedMasters.add(key);
463
+ estTriangles += obj.tris.length / 3;
464
+ }
465
+ checkInstances();
451
466
  }
452
467
  for (const comp of obj.components) {
453
468
  if (comp.path !== undefined) {
454
469
  // External production-extension part: estimate from its directory size WITHOUT decompressing it.
455
- const entry = entryByName(comp.path.replace(/^\//, '').toLowerCase());
470
+ const norm = comp.path.replace(/^\//, '').toLowerCase();
471
+ const entry = entryByName(norm);
456
472
  if (entry !== undefined) {
457
473
  instanceCount++;
458
- estTriangles += entry.uncompressedSize / EST_MAX_BYTES_PER_TRIANGLE;
474
+ if (!countedMasters.has(norm)) {
475
+ countedMasters.add(norm);
476
+ estTriangles += entry.uncompressedSize / EST_MAX_BYTES_PER_TRIANGLE;
477
+ }
478
+ checkInstances();
459
479
  }
460
480
  }
461
481
  else {
462
482
  visit(part, comp.objectid, depth + 1); // local sub-object (already-parsed geometry)
463
483
  }
464
- if (instanceCount > lim.maxInstances) {
465
- throw new ModelParseError('E_MODEL_TOO_MANY_INSTANCES', `3MF declares more than ${lim.maxInstances} instance placements`);
466
- }
467
484
  }
468
485
  };
469
486
  const roots = mainPart.items.length > 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chestnutlabs/gcode-model-renderer",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Three.js presentation renderer for source models (STL/3MF) — clean thumbnails/cards, distinct from toolpath inspection (DD-018).",
5
5
  "keywords": [
6
6
  "3d-printing",
@@ -51,9 +51,9 @@
51
51
  "test": "vitest run"
52
52
  },
53
53
  "dependencies": {
54
- "@chestnutlabs/gcode-containers": "0.11.0",
55
- "@chestnutlabs/gcode-renderer-three": "0.11.0",
56
- "@chestnutlabs/toolpath-core": "0.11.0"
54
+ "@chestnutlabs/gcode-containers": "0.12.0",
55
+ "@chestnutlabs/gcode-renderer-three": "0.12.0",
56
+ "@chestnutlabs/toolpath-core": "0.12.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@types/three": "0.178.0"