- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
53 lines
2.6 KiB
Python
53 lines
2.6 KiB
Python
"""Fingerprint a .3dm (run inside Rhino 8 via runner). Globals: MODEL_PATH, OUT_PATH, optional FAST.
|
|
|
|
Opens MODEL_PATH headless (the active doc is untouched) and writes, per object id (user string `id`,
|
|
falling back to the object name): layer path, geometry type, visibility, colour, bbox (0.01 mm),
|
|
brep face count / solidity / volume (mm3, 1 decimal), curve length (0.001 mm) and all user strings.
|
|
Hidden and locked objects are included.
|
|
"""
|
|
import json
|
|
|
|
import Rhino
|
|
import Rhino.Geometry as rg
|
|
from Rhino.DocObjects import ObjectEnumeratorSettings
|
|
|
|
FAST = globals().get("FAST", False)
|
|
d = Rhino.RhinoDoc.OpenHeadless(MODEL_PATH)
|
|
if d is None:
|
|
raise RuntimeError(f"cannot open {MODEL_PATH}")
|
|
try:
|
|
s = ObjectEnumeratorSettings()
|
|
s.HiddenObjects = True
|
|
s.LockedObjects = True
|
|
objs, layers = {}, {}
|
|
for o in d.Objects.GetObjectList(s):
|
|
a = o.Attributes
|
|
oid = a.GetUserString("id") or a.Name or str(o.Id)
|
|
lay = d.Layers[a.LayerIndex]
|
|
layers[lay.FullPath] = [lay.Color.R, lay.Color.G, lay.Color.B, lay.IsVisible]
|
|
g = o.Geometry
|
|
bb = g.GetBoundingBox(True)
|
|
rec = {"layer": lay.FullPath, "type": type(g).__name__, "hidden": o.IsHidden, "locked": o.IsLocked,
|
|
"colour_source": int(a.ColorSource), "colour": [a.ObjectColor.R, a.ObjectColor.G, a.ObjectColor.B],
|
|
"bbox": [round(v, 2) for v in (bb.Min.X, bb.Min.Y, bb.Min.Z, bb.Max.X, bb.Max.Y, bb.Max.Z)],
|
|
"user": {k: a.GetUserString(k) for k in sorted(a.GetUserStrings().AllKeys)}}
|
|
b = g if isinstance(g, rg.Brep) else (rg.Brep.TryConvertBrep(g) if isinstance(g, rg.Extrusion) else None)
|
|
if b is not None:
|
|
rec["faces"], rec["solid"] = b.Faces.Count, b.IsSolid
|
|
if FAST and "-R-" in oid: # rebar pipe: bbox + face count only (volume dominates run time)
|
|
rec["volume"] = "skipped"
|
|
else:
|
|
mp = rg.VolumeMassProperties.Compute(b) if b.IsSolid else None
|
|
rec["volume"] = round(mp.Volume, 1) if mp else None
|
|
elif isinstance(g, rg.Curve):
|
|
rec["length"] = round(g.GetLength(), 3)
|
|
if oid in objs:
|
|
oid = f"{oid}#dup{sum(k.startswith(oid) for k in objs)}"
|
|
objs[oid] = rec
|
|
out = {"model": MODEL_PATH, "units": str(d.ModelUnitSystem), "tolerance": d.ModelAbsoluteTolerance,
|
|
"count": len(objs), "layers": layers, "objects": objs}
|
|
json.dump(out, open(OUT_PATH, "w", encoding="utf-8"), ensure_ascii=False)
|
|
print(json.dumps({"count": len(objs), "units": out["units"], "tolerance": out["tolerance"]}))
|
|
finally:
|
|
d.Dispose()
|