printhive/tools/threemf_info.py
dlawler489 fcb418f119 Show 3MF projects plate by plate in their filament colours
- threemf_stream reads Bambu Studio / OrcaSlicer project metadata: plates
  (name, objects, preview images), filaments, and each part's type and
  extruder. Placements now record which build item and part they belong to.
- convert_3mf.py leaves out negative/modifier/support parts, colours each
  part by its filament (a part's extruder 0 falls back to its object's), and
  takes --plate N. Geometry is still stored once per (part, colour).
- New threemf_info.py lists plates and filaments without loading geometry
  and extracts plate preview images; served via /api/{files,entries}/:id/project.
- /glb?plate=N builds and caches a per-plate preview. Cached GLB names carry
  a converter version, so previews made before colours are rebuilt.
- Model detail: plate chips (with the slicer's plate images) under the
  viewer — multi-plate projects open on plate 1 — plus "All plates", and a
  Filaments section with colour swatches.
- Fingerprints leave out negative/modifier parts, so a project matches the
  model it prints.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 07:49:58 +10:00

44 lines
1.5 KiB
Python

#!/usr/bin/env python3
"""Print a 3MF project's plates and filaments as JSON, without loading geometry.
Usage: threemf_info.py <file.3mf> [--images DIR]
With --images, each plate's preview is written to DIR/<n>.png and DIR/<n>-small.png.
"""
import argparse
import json
import os
import zipfile
import threemf_stream
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input")
parser.add_argument("--images")
args = parser.parse_args()
try:
project = threemf_stream.read_project(args.input)
if args.images and project["plates"]:
os.makedirs(args.images, exist_ok=True)
with zipfile.ZipFile(args.input) as z:
for plate in project["plates"]:
for name, suffix in ((plate["thumbnail"], ""), (plate["thumbnail_small"], "-small")):
if name:
with open(os.path.join(args.images, f"{plate['index']}{suffix}.png"), "wb") as out:
out.write(z.read(name))
except Exception as exc:
print(json.dumps({"error": f"could not read 3MF: {str(exc)[:200]}"}))
return 1
print(json.dumps({
"plates": [{"index": p["index"], "name": p["name"], "objectCount": len(p["objects"]),
"thumbnail": bool(p["thumbnail"] or p["thumbnail_small"])} for p in project["plates"]],
"filaments": project["filaments"],
}))
return 0
if __name__ == "__main__":
raise SystemExit(main())