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:
ahnyongjooandClaude Sonnet 5.5 committed 2026-10-28 11:20:18 +09:00
commit 964a841de1
3978 files changed
+58013356

No files matched your search

@@ -0,0 +1,181 @@
"""G3 gate checker, form "shaft cage" (Ramp-A P1, 2026-10-13): drilled-shaft cage bars and the anti-uplift plate.
Globals: __rhino_doc__, EXPECTED_PATH, RESULT_PATH. Expected values (section G3) come from the D4 schedule (L, N),
the 구조물 치수표 chains measured from the shaft top EL (D1), the section radii and the user decisions S1-S6;
generator params are not read.
Checks (C9): counts, original length (+coupler) vs schedule L, main radius / division / piece levels, hoop and
tie level grids, hoop radius, X / Y ties apart, U bars (count, bottom level, width, planes), plate (size,
thickness, centre, cover), containment in the pile segments, min side cover (record).
"""
import json
import math
import os
import sys
import Rhino.Geometry as rg
sys.path.insert(0, os.path.dirname(__file__))
from _common import all_objects, load_expected, write_result # noqa: E402
doc = __rhino_doc__
X = load_expected(EXPECTED_PATH)
PID, TOL, G = X["pier_id"], X["tol_mm"], X["G3"]
SCHED_L, SCHED_N, DIA = G["schedule_L"], G["schedule_n"], G["dia"]
M, HP, TI, U, PL = G["main"], G["hoops"], G["ties"], G["u_bars"], G["plate"]
TOP = G["shaft_top"]
PILE = X["G1"]["pile"]
Z_BOT = PILE["z_bottom"]
SEG = [] # (z0, z1, radius) bottom to top
z = Z_BOT
for s in PILE["segments"]:
SEG.append((z, z + s["length"], s["d"] / 2))
z += s["length"]
def chain(c):
"""Levels from the shaft top: first offset, then (count, pitch) groups downwards; returned bottom to top."""
out = [TOP - c["first"]]
for n, p in c["groups"]:
out += [out[-1] - p * k for k in range(1, n + 1)]
return sorted(out)
HOOP_LEVELS, TIE_LEVELS = chain(HP["chain"]), chain(TI["chain"])
results = []
def check(item, exp, act, tol=TOL):
results.append(dict(check="C9", 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="C9", item=item, detail=detail, result="PASS" if ok else "FAIL"))
def info(item, detail):
results.append(dict(check="C9", item=item, detail=detail, result="INFO"))
def ids(prefix):
return [o for o in all_objects(doc) if (o.Attributes.GetUserString("id") or "").startswith(prefix)]
def main():
cl = {m: [o for o in ids(f"{PID}-RC-") if o.Attributes.GetUserString("mark") == m] for m in SCHED_L}
pipes = {m: [o for o in ids(f"{PID}-R-") if o.Attributes.GetUserString("mark") == m] for m in SCHED_L}
occ = lambda o: o.Attributes.GetUserString("occurrence").rstrip("ab")
hm, tm = HP["mark"], TI["mark"]
for m in SCHED_L:
n_occ = len({occ(o) for o in cl[m]}) if m == hm else len(cl[m])
check(f"{m} bar count", SCHED_N[m], n_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"{len(pipes[m])}/{len(cl[m])}")
for m in SCHED_L:
per, curve = {}, {}
for o in cl[m]:
k = occ(o) if m == hm else o.Attributes.GetUserString("occurrence")
cp = float(o.Attributes.GetUserString("coupler_length") or 0)
per[k] = per.get(k, 0) + float(o.Attributes.GetUserString("design_length")) + cp
curve[k] = curve.get(k, 0) + o.Geometry.GetLength() + cp
if per:
check(f"{m} original length (+coupler) vs schedule L (worst of {len(per)})", 0,
max(abs(v - SCHED_L[m]) for v in per.values()))
dev = [v - SCHED_L[m] for v in curve.values()]
info(f"{m} curve length (+coupler) - L (record)", f"min {min(dev):.1f} / max {max(dev):.1f} mm")
# main bars: pieces stacked between the first and the last hoop level (S6: coupler length 0)
zb, zt = HOOP_LEVELS[0], HOOP_LEVELS[-1]
check("main span (last - first hoop level) = sum of piece L", zt - zb, sum(SCHED_L[mk] for mk in M["pieces"]))
z = zb
for mk in M["pieces"]: # bottom to top
lo = [min(o.Geometry.PointAtStart.Z, o.Geometry.PointAtEnd.Z) for o in cl[mk]]
hi = [max(o.Geometry.PointAtStart.Z, o.Geometry.PointAtEnd.Z) for o in cl[mk]]
check(f"{mk} bottom EL (worst)", 0, max(abs(v - z) for v in lo))
check(f"{mk} top EL = bottom + L (worst)", 0, max(abs(v - (z + SCHED_L[mk])) for v in hi))
z += SCHED_L[mk]
rr = [math.hypot(o.Geometry.PointAtStart.X, o.Geometry.PointAtStart.Y) for o in cl[mk]]
check(f"{mk} radius (worst)", 0, max(abs(r - M["radius"]) for r in rr), tol=0.5)
a = sorted(math.degrees(math.atan2(o.Geometry.PointAtStart.Y, o.Geometry.PointAtStart.X)) % 360 for o in cl[mk])
check(f"{mk} angle vs {M['start_deg']} + i*360/{M['divisions']} (worst, deg)", 0,
max(abs(x - (M["start_deg"] + i * 360 / M["divisions"])) for i, x in enumerate(a)), tol=0.01)
# hoops
hz = {}
for o in cl[hm]:
bb = o.Geometry.GetBoundingBox(True)
hz.setdefault(round((bb.Max.Z + bb.Min.Z) / 2, 1), []).append(o)
zs = sorted(hz)
flag(f"{hm} levels {len(HOOP_LEVELS)} x 2 pieces", len(zs) == len(HOOP_LEVELS) and all(len(v) == 2 for v in hz.values()),
f"{len(zs)} levels")
check(f"{hm} levels vs 구조물 치수표 chain (worst)", 0,
max(abs(a - b) for a, b in zip(zs, HOOP_LEVELS)) if len(zs) == len(HOOP_LEVELS) else 1e9)
for o in cl[hm][:4]:
c = o.Geometry
p = c.PointAt(c.Domain.Mid)
check(f"{hm} radius at mid point ({o.Attributes.GetUserString('occurrence')})", HP["radius"], math.hypot(p.X, p.Y), tol=0.5)
# ties
by = lambda s: sorted((o.Geometry for o in cl[tm] if o.Attributes.GetUserString("occurrence").endswith(s)),
key=lambda g: g.GetBoundingBox(True).Max.Z)
xs, ys = by("X"), by("Y")
txm = [g.GetBoundingBox(True).Max.Z for g in xs]
tym = [g.GetBoundingBox(True).Max.Z for g in ys]
flag(f"{tm} X / Y per level, {len(TIE_LEVELS)} levels", len(txm) == len(tym) == len(TIE_LEVELS), f"X {len(txm)} Y {len(tym)}")
if len(txm) == len(TIE_LEVELS) == len(tym):
check(f"{tm} X levels vs chain (worst)", 0, max(abs(a - b) for a, b in zip(txm, TIE_LEVELS)))
check(f"{tm} Y levels = X + {TI['y_lift']} (worst)", 0, max(abs(a - b - TI["y_lift"]) for a, b in zip(tym, TIE_LEVELS)))
gaps = []
for a, b in zip(xs, ys):
ok, pa, pb = a.ClosestPoints(b)
gaps.append(pa.DistanceTo(pb) if ok else 0.0)
gap = min(gaps) if gaps else 0.0
check(f"{tm} X / Y centrelines apart >= bar diameter {DIA[tm]} (min, capped)", DIA[tm], min(gap, DIA[tm]), tol=0.01)
# U bars: bottom level, overall width (vertical legs on the hoop circle), one plane each through the centre
um = U["mark"]
bots = [o.Geometry.GetBoundingBox(True).Min.Z for o in cl[um]]
check(f"{um} bottom EL = shaft bottom + {U['bottom_above']} (worst, centreline)", 0,
max(abs(b - (Z_BOT + U["bottom_above"])) for b in bots) if bots else 1e9)
w, planar = [], 0
for o in cl[um]:
c = o.Geometry
a, b = c.PointAtStart, c.PointAtEnd
w.append(math.hypot(a.X - b.X, a.Y - b.Y))
ok, pl = c.TryGetPlane(0.01)
if ok and abs(pl.Normal.Z) < 1e-6 and abs(pl.DistanceTo(rg.Point3d(0, 0, pl.Origin.Z))) < 0.5:
planar += 1
check(f"{um} leg spacing (worst) = 2 x hoop radius {2 * HP['radius']:.0f}", 0, max(abs(x - 2 * HP["radius"]) for x in w) if w else 1e9)
check(f"{um} vertical planes through the centre", len(cl[um]), planar, tol=0)
# plate
pts = ids(f"{PID}-PT")
flag("plate count 1", len(pts) == 1, str(len(pts)))
if pts:
bb = pts[0].Geometry.GetBoundingBox(True)
check("plate size X", PL["size"], bb.Max.X - bb.Min.X)
check("plate size Y", PL["size"], bb.Max.Y - bb.Min.Y)
check("plate thickness", PL["thickness"], bb.Max.Z - bb.Min.Z, tol=0.01)
check("plate centre X / Y offset", 0, math.hypot((bb.Max.X + bb.Min.X) / 2, (bb.Max.Y + bb.Min.Y) / 2))
check(f"plate top = U bottom - {DIA[um] / 2:.0f} (S4)", Z_BOT + U["bottom_above"] - DIA[um] / 2, bb.Max.Z, tol=0.01)
info("plate bottom cover (record)", f"{bb.Min.Z - Z_BOT:.1f} mm")
# containment and side cover in the pile segments
solids = [o.Geometry for o in all_objects(doc) if (o.Attributes.GetUserString("id") or "").startswith(f"{PID}-PL")]
min_cover = {}
for m in SCHED_L:
bad, cov = 0, 1e9
r = DIA[m] / 2
for o in cl[m]:
c = o.Geometry
for t in c.DivideByCount(max(4, int(c.GetLength() / 150)), 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 * r, p - radial * r, p + rg.Vector3d(0, 0, r), p - rg.Vector3d(0, 0, r)):
if not any(s.IsPointInside(q, 0.01, False) for s in solids):
bad += 1
R = next((rad for z0, z1, rad in SEG if z0 <= p.Z <= z1), None)
if R is not None:
cov = min(cov, R - math.hypot(p.X, p.Y) - r, p.Z - Z_BOT - r)
min_cover[m] = round(cov, 1)
check(f"{m} sample points outside the pile concrete", 0, bad, tol=0)
info("min cover per mark (side or bottom, record)", json.dumps(min_cover))
write_result(RESULT_PATH, results, dict(min_cover=min_cover))
main()