- 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>
44 lines
1.5 KiB
Python
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())
|