chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- 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>
This commit is contained in:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,182 @@
|
||||
"""Verify column rebar P1-P6 (check C8) inside Rhino 8. Independent expected values typed from
|
||||
D3 schedule (L, counts) and D1 dimension chains / section; params.json is not read.
|
||||
|
||||
Globals: __rhino_doc__, RESULT_PATH; optional FINGERPRINT_REF (json of non-rebar objects) or
|
||||
FINGERPRINT_OUT (write fingerprints of non-rebar objects and stop).
|
||||
"""
|
||||
import json
|
||||
import math
|
||||
|
||||
import Rhino.Geometry as rg
|
||||
|
||||
doc = __rhino_doc__
|
||||
|
||||
|
||||
def ALL_OBJECTS():
|
||||
"""All live objects incl. hidden/locked (default doc.Objects skips hidden ones; 2026-10-07 P1-FT hidden)."""
|
||||
from Rhino.DocObjects import ObjectEnumeratorSettings
|
||||
s = ObjectEnumeratorSettings()
|
||||
s.HiddenObjects = True
|
||||
s.LockedObjects = True
|
||||
return list(doc.Objects.GetObjectList(s))
|
||||
TOL = 1.0
|
||||
SCHED_L = {"P1": 17247, "P2": 16553, "P3": 6436, "P4": 5636, "P5": 5636, "P6": 2136} # D3 L (mm)
|
||||
SCHED_N = {"P1": 46, "P2": 46, "P3": 36, "P4": 9, "P5": 22, "P6": 36} # D3 수량
|
||||
DIA = {"P1": 32, "P2": 32, "P3": 19, "P4": 19, "P5": 19, "P6": 16}
|
||||
FB = 26208 # D1 'EL. 26.208'
|
||||
LEVELS = [FB + 150 + 1030 + 120 * i for i in range(9)] # '150','1.030','8@120=960'
|
||||
LEVELS += [LEVELS[-1] + 120 * (i + 1) for i in range(22)] # '22@120=2.640'
|
||||
LEVELS += [LEVELS[-1] + 300 * (i + 1) for i in range(36)] # '36@300=10.800'
|
||||
TIE_LEVELS = [FB + 150 + 1030 + 1800 * k for k in range(9)] # '8@1.800=14.400'
|
||||
R_OUT, R_IN, R_HOOP = 870.5, 825.5, 897 # HITL H2 (section CAD) / D3 R
|
||||
TOP = FB + 300 + 340 + 16213 # P1: leg z + R + a (= 43.061)
|
||||
results = []
|
||||
|
||||
|
||||
def check(item, exp, act, tol=TOL):
|
||||
results.append(dict(check="C8", item=item, expected=round(exp, 3), actual=round(act, 3),
|
||||
diff=round(act - exp, 3), tol=tol, result="PASS" if abs(act - exp) <= tol else "FAIL"))
|
||||
|
||||
|
||||
def flag(item, ok, detail):
|
||||
results.append(dict(check="C8", item=item, detail=detail, result="PASS" if ok else "FAIL"))
|
||||
|
||||
|
||||
def ids(prefix):
|
||||
return [o for o in ALL_OBJECTS() if (o.Attributes.GetUserString("id") or "").startswith(prefix)]
|
||||
|
||||
|
||||
def fingerprints():
|
||||
fp = {}
|
||||
for o in ALL_OBJECTS():
|
||||
oid = o.Attributes.GetUserString("id")
|
||||
if oid and not oid.startswith("P1-R"):
|
||||
b = o.Geometry if isinstance(o.Geometry, rg.Brep) else rg.Brep.TryConvertBrep(o.Geometry)
|
||||
bb = b.GetBoundingBox(True)
|
||||
fp[oid] = [round(v, 3) for v in (bb.Min.X, bb.Min.Y, bb.Min.Z, bb.Max.X, bb.Max.Y, bb.Max.Z,
|
||||
rg.VolumeMassProperties.Compute(b).Volume / 1e6)]
|
||||
return fp
|
||||
|
||||
|
||||
def concrete():
|
||||
out = []
|
||||
for oid in ("P1-CL", "P1-FT", "P1-CP"):
|
||||
o = [x for x in ALL_OBJECTS() if x.Attributes.GetUserString("id") == oid][0]
|
||||
out.append(o.Geometry if isinstance(o.Geometry, rg.Brep) else rg.Brep.TryConvertBrep(o.Geometry))
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
if "FINGERPRINT_OUT" in globals():
|
||||
json.dump(fingerprints(), open(FINGERPRINT_OUT, "w", encoding="utf-8"), indent=0)
|
||||
print("fingerprints written", len(fingerprints()))
|
||||
return
|
||||
cl = {m: [o for o in ids("P1-RC-") if o.Attributes.GetUserString("mark") == m] for m in SCHED_L}
|
||||
pipes = {m: [o for o in ids("P1-R-") if o.Attributes.GetUserString("mark") == m] for m in SCHED_L}
|
||||
# counts (bars = occurrences; P3/P4 consist of 2 pieces per level)
|
||||
for m in SCHED_L:
|
||||
occ = {o.Attributes.GetUserString("occurrence").rstrip("ab") for o in cl[m]}
|
||||
check(f"{m} bar count", SCHED_N[m], len(occ), tol=0)
|
||||
flag(f"{m} pipes closed & paired with centrelines",
|
||||
len(pipes[m]) == len(cl[m]) and all(o.Geometry.IsSolid for o in pipes[m]),
|
||||
f"pipes {len(pipes[m])} / centrelines {len(cl[m])}")
|
||||
# original (design) length per bar vs schedule L
|
||||
for m in SCHED_L:
|
||||
per = {}
|
||||
for o in cl[m]:
|
||||
k = o.Attributes.GetUserString("occurrence").rstrip("ab")
|
||||
per[k] = per.get(k, 0) + float(o.Attributes.GetUserString("design_length")) + float(
|
||||
o.Attributes.GetUserString("coupler_length") or 0) # schedule L includes coupler b (D3 V, IX)
|
||||
worst = max(abs(v - SCHED_L[m]) for v in per.values())
|
||||
check(f"{m} original length (+coupler b) vs schedule L (worst of {len(per)})", 0, worst)
|
||||
curve = {}
|
||||
for o in cl[m]:
|
||||
k = o.Attributes.GetUserString("occurrence").rstrip("ab")
|
||||
curve[k] = curve.get(k, 0) + o.Geometry.GetLength() + float(
|
||||
o.Attributes.GetUserString("coupler_length") or 0)
|
||||
dev = [v - SCHED_L[m] for v in curve.values()]
|
||||
results.append(dict(check="C8", item=f"{m} curve length - L (record, fillets shorten hooks)",
|
||||
detail=f"min {min(dev):.1f} / max {max(dev):.1f} mm", result="INFO"))
|
||||
if m in ("P1", "P2", "P4", "P5"): # no fillets: curve length must equal schedule
|
||||
check(f"{m} curve length (+coupler b) vs L (no fillet)", 0, max(abs(d) for d in dev))
|
||||
# main bar geometry
|
||||
for m, r in (("P1", R_OUT), ("P2", R_IN)):
|
||||
angs, worst_r = [], 0
|
||||
for o in cl[m]:
|
||||
c = o.Geometry
|
||||
p = c.PointAtEnd if c.PointAtEnd.Z > c.PointAtStart.Z else c.PointAtStart
|
||||
angs.append(math.degrees(math.atan2(p.Y, p.X)) % 360)
|
||||
worst_r = max(worst_r, abs(math.hypot(p.X, p.Y) - r))
|
||||
check(f"{m} top EL", TOP, p.Z) if o is cl[m][0] else None
|
||||
angs.sort()
|
||||
worst_a = max(abs(a - i * 360 / 46) for i, a in enumerate(angs))
|
||||
check(f"{m} radius (worst)", 0, worst_r, tol=0.5)
|
||||
check(f"{m} angle vs i*360/46 (worst, deg)", 0, worst_a, tol=0.01)
|
||||
p1b = min(min(o.Geometry.PointAtStart.Z, o.Geometry.PointAtEnd.Z) for o in cl["P1"])
|
||||
p2b = min(min(o.Geometry.PointAtStart.Z, o.Geometry.PointAtEnd.Z) for o in cl["P2"])
|
||||
check("P1 hook leg EL (footing bottom + 300)", FB + 300, p1b)
|
||||
check("P2 bottom EL", FB + 300, p2b)
|
||||
# hoop levels and radius
|
||||
hz = {}
|
||||
for m in ("P3", "P4", "P5"):
|
||||
for o in cl[m]:
|
||||
bb = o.Geometry.GetBoundingBox(True)
|
||||
hz.setdefault(round(bb.Max.Z, 1), set()).add(m)
|
||||
if m != "P3":
|
||||
p = o.Geometry.PointAtStart
|
||||
check(f"{m} radius", R_HOOP, math.hypot(p.X, p.Y), tol=0.5) if o is cl[m][0] else None
|
||||
zs = sorted(hz)
|
||||
flag("hoop level count 67", len(zs) == 67, f"{len(zs)}")
|
||||
check("hoop levels vs dim chain (worst)", 0, max(abs(a - b) for a, b in zip(zs, LEVELS)))
|
||||
marks_by_level = [list(hz[z])[0] for z in zs]
|
||||
flag("hoop marks by zone (P4 x9, P5 x22, P3 x36)",
|
||||
marks_by_level == ["P4"] * 9 + ["P5"] * 22 + ["P3"] * 36, "")
|
||||
tz = sorted({round(o.Geometry.GetBoundingBox(True).Max.Z, 1) for o in cl["P6"]})
|
||||
check("tie levels vs chain + 17.5 (worst)", 0, max(abs(a - (b + 17.5)) for a, b in zip(tz, TIE_LEVELS)))
|
||||
# containment in concrete union (centreline samples offset by bar radius) and min cover
|
||||
solids = concrete()
|
||||
union = rg.Brep.CreateBooleanUnion(solids, 0.01)
|
||||
shell = union[0] if union and len(union) == 1 else None # outer surface only (no internal joint faces)
|
||||
results.append(dict(check="C8", item="concrete union for cover", detail=f"{len(union) if union else 0} brep",
|
||||
result="INFO"))
|
||||
outside, min_cover = {}, {}
|
||||
for m in SCHED_L:
|
||||
bad, cov = 0, 1e9
|
||||
for o in cl[m]:
|
||||
c = o.Geometry
|
||||
n = max(4, int(c.GetLength() / 100))
|
||||
for t in c.DivideByCount(n, True):
|
||||
p = c.PointAt(t)
|
||||
radial = rg.Vector3d(p.X, p.Y, 0)
|
||||
radial.Unitize() if radial.Length > 1e-6 else None
|
||||
for q in (p, p + radial * (DIA[m] / 2), p - radial * (DIA[m] / 2),
|
||||
p + rg.Vector3d(0, 0, DIA[m] / 2), p - rg.Vector3d(0, 0, DIA[m] / 2)):
|
||||
if not any(s.IsPointInside(q, 0.01, False) for s in solids):
|
||||
bad += 1
|
||||
d = p.DistanceTo(shell.ClosestPoint(p)) if shell and shell.IsPointInside(p, 0.01, False) else -1
|
||||
cov = min(cov, d - DIA[m] / 2)
|
||||
outside[m] = bad
|
||||
min_cover[m] = round(cov, 1)
|
||||
check(f"{m} sample points outside concrete", 0, bad, tol=0)
|
||||
results.append(dict(check="C8", item="min cover to concrete surface per mark (record)",
|
||||
detail=json.dumps(min_cover), result="INFO"))
|
||||
results.append(dict(check="C8", item="clearance P1 outer surface vs hoop inner surface (record)",
|
||||
detail=f"{R_HOOP - 19 / 2 - (R_OUT + 16):.1f} mm", result="INFO"))
|
||||
if "FINGERPRINT_REF" in globals():
|
||||
ref = json.load(open(FINGERPRINT_REF, encoding="utf-8"))
|
||||
now = fingerprints()
|
||||
worst = max(abs(a - b) for k in ref for a, b in zip(ref[k], now.get(k, [1e9] * 7)))
|
||||
flag("existing non-rebar objects unchanged", set(ref) == set(now) and worst <= 0.001,
|
||||
f"{len(now)}/{len(ref)} max diff {worst}")
|
||||
summ = dict(total=len([r for r in results if r["result"] != "INFO"]),
|
||||
passed=sum(r["result"] == "PASS" for r in results),
|
||||
failed=sum(r["result"] == "FAIL" for r in results), min_cover=min_cover)
|
||||
json.dump(dict(summary=summ, results=results), open(RESULT_PATH, "w", encoding="utf-8"),
|
||||
ensure_ascii=False, indent=1)
|
||||
print(json.dumps(summ, ensure_ascii=False))
|
||||
for r in results:
|
||||
if r["result"] in ("FAIL", "INFO"):
|
||||
print(r["result"], r["item"], r.get("detail", r.get("diff")))
|
||||
|
||||
|
||||
main()
|
||||
Reference in new issue
Block a user