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,156 @@
|
||||
"""G2 gate checker, form "ring" (Ulsan P1, 2026-10-13): column rebar with one main ring, hoop zones, cross ties.
|
||||
|
||||
Globals: __rhino_doc__, EXPECTED_PATH, RESULT_PATH. Expected values (section G2 + G1 footing / column) come
|
||||
from the schedule (L, counts, R) and the dimension chains of the rebar drawing; generator params are not read.
|
||||
Checks (C8): counts, original length (+coupler) vs schedule L, curve length for bars without fillets, main bar
|
||||
radius / division / leg and top level, hoop level chain / zones / radius, tie levels / count per level,
|
||||
containment in concrete and minimum 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, chain, load_expected, write_result # noqa: E402
|
||||
|
||||
doc = __rhino_doc__
|
||||
X = load_expected(EXPECTED_PATH)
|
||||
PID, TOL, G = X["pier_id"], X["tol_mm"], X["G2"]
|
||||
SCHED_L, SCHED_N, DIA = G["schedule_L"], G["schedule_n"], G["dia"]
|
||||
FB = X["G1"]["footing"]["z0"]
|
||||
M = G["main"]
|
||||
LEVELS = chain(G["hoop_chain"], FB)
|
||||
TIE = G["tie"]
|
||||
TIE_LEVELS = chain(TIE["chain"], FB)
|
||||
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 info(item, detail):
|
||||
results.append(dict(check="C8", 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")
|
||||
tie_mark = TIE["mark"]
|
||||
for m in SCHED_L:
|
||||
n_occ = len(cl[m]) if m == tie_mark else len({occ(o) for o in 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 = o.Attributes.GetUserString("occurrence") if m == tie_mark else occ(o)
|
||||
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 not per:
|
||||
continue
|
||||
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 - L (record)", f"min {min(dev):.1f} / max {max(dev):.1f} mm")
|
||||
if m in G["no_fillet_marks"]:
|
||||
check(f"{m} curve length (+coupler) vs L (no fillet)", 0, max(abs(d) for d in dev))
|
||||
# main bars
|
||||
mm_, R0, ND = M["mark"], M["radius"], M["divisions"]
|
||||
angs, worst_r, tops, legs = [], 0, [], []
|
||||
for o in cl[mm_]:
|
||||
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) - R0))
|
||||
tops.append(p.Z)
|
||||
legs.append(min(c.PointAtStart.Z, c.PointAtEnd.Z))
|
||||
angs.sort()
|
||||
check(f"{mm_} radius (worst)", 0, worst_r, tol=0.5)
|
||||
check(f"{mm_} angle vs start + i*360/{ND} (worst, deg)", 0,
|
||||
max(abs(a - (M.get("start_deg", 0) + i * 360 / ND)) for i, a in enumerate(angs)), tol=0.01)
|
||||
check(f"{mm_} top EL (worst)", 0, max(abs(t - (FB + sum(M["top_parts"]))) for t in tops))
|
||||
check(f"{mm_} hook leg EL (footing bottom + {M['bottom_above_footing']}) (worst)", 0,
|
||||
max(abs(z - (FB + M["bottom_above_footing"])) for z in legs))
|
||||
# hoops
|
||||
hz = {}
|
||||
for m, _ in G["hoop_zones"]:
|
||||
for o in cl[m]:
|
||||
hz.setdefault(round(o.Geometry.GetBoundingBox(True).Max.Z, 1), set()).add(m)
|
||||
zs = sorted(hz)
|
||||
flag(f"hoop level count {len(LEVELS)}", len(zs) == len(LEVELS), f"{len(zs)}")
|
||||
check("hoop levels vs dim chain (worst)", 0, max(abs(a - b) for a, b in zip(zs, LEVELS)) if zs else 1e9)
|
||||
flag("hoop marks by zone (" + ", ".join(f"{m} x{n}" for m, n in G["hoop_zones"]) + ")",
|
||||
[list(hz[z])[0] for z in zs] == [m for m, n in G["hoop_zones"] for _ in range(n)])
|
||||
for m, _ in G["hoop_zones"]:
|
||||
o = cl[m][0]
|
||||
p = o.Geometry.PointAt(o.Geometry.Domain.Mid)
|
||||
check(f"{m} radius (mid point)", G.get("r_hoop_by_mark", {}).get(m, G["r_hoop"]), math.hypot(p.X, p.Y), tol=0.5)
|
||||
if "inner" in M: # Okseong P1: inner ring (2026-10-14)
|
||||
im = M["inner"]
|
||||
tops = [max((o.Geometry.PointAtStart, o.Geometry.PointAtEnd), key=lambda q: q.Z) for o in cl[im["mark"]]]
|
||||
lows = [min((o.Geometry.PointAtStart, o.Geometry.PointAtEnd), key=lambda q: q.Z) for o in cl[im["mark"]]]
|
||||
check(f"{im['mark']} inner ring radius (worst)", 0, max(abs(math.hypot(q.X, q.Y) - im["radius"]) for q in tops) if tops else 1e9, tol=0.5)
|
||||
flag(f"{im['mark']} hooks bent inwards", bool(lows) and all(math.hypot(q.X, q.Y) < im["radius"] for q in lows))
|
||||
# ties
|
||||
if TIE.get("y_lift"): # Okseong P1: X tie at the level, Y tie lifted
|
||||
occ_ = lambda o: o.Attributes.GetUserString("occurrence")
|
||||
lx = sorted(round(o.Geometry.GetBoundingBox(True).Max.Z, 1) for o in cl[tie_mark] if occ_(o).endswith("X"))
|
||||
ly = sorted(round(o.Geometry.GetBoundingBox(True).Max.Z, 1) for o in cl[tie_mark] if occ_(o).endswith("Y"))
|
||||
flag(f"tie X / Y levels {len(TIE_LEVELS)} each", len(lx) == len(ly) == len(TIE_LEVELS), f"X {len(lx)} Y {len(ly)}")
|
||||
check("tie X levels vs chain (worst)", 0, max(abs(a - b - TIE["offset"]) for a, b in zip(lx, TIE_LEVELS)) if lx else 1e9)
|
||||
check(f"tie Y = X + {TIE['y_lift']} (worst)", 0, max(abs(b - a - TIE["y_lift"]) for a, b in zip(lx, ly)) if ly else 1e9)
|
||||
else:
|
||||
tz = {}
|
||||
for o in cl[tie_mark]:
|
||||
tz.setdefault(round(o.Geometry.GetBoundingBox(True).Max.Z, 1), []).append(o)
|
||||
lv = sorted(tz)
|
||||
flag(f"tie levels {len(TIE_LEVELS)} x {TIE['per_level']}", len(lv) == len(TIE_LEVELS)
|
||||
and all(len(v) == TIE["per_level"] for v in tz.values()), f"{len(lv)} levels")
|
||||
check(f"tie levels vs chain + {TIE['offset']} (worst)", 0,
|
||||
max(abs(a - (b + TIE["offset"])) for a, b in zip(lv, TIE_LEVELS)) if lv else 1e9)
|
||||
# containment and cover
|
||||
solids = []
|
||||
for oid in (f"{PID}-CL", f"{PID}-FT", f"{PID}-CP"):
|
||||
o = [x for x in all_objects(doc) if x.Attributes.GetUserString("id") == oid][0]
|
||||
solids.append(o.Geometry if isinstance(o.Geometry, rg.Brep) else rg.Brep.TryConvertBrep(o.Geometry))
|
||||
union = rg.Brep.CreateBooleanUnion(solids, 0.01)
|
||||
shell = union[0] if union and len(union) == 1 else None
|
||||
info("concrete union for cover", f"{len(union) if union else 0} brep")
|
||||
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
|
||||
d = p.DistanceTo(shell.ClosestPoint(p)) if shell and shell.IsPointInside(p, 0.01, False) else -1
|
||||
cov = min(cov, d - r)
|
||||
min_cover[m] = round(cov, 1)
|
||||
check(f"{m} sample points outside concrete", 0, bad, tol=0)
|
||||
info("min cover to concrete surface per mark (record)", json.dumps(min_cover))
|
||||
write_result(RESULT_PATH, results, dict(min_cover=min_cover))
|
||||
|
||||
|
||||
main()
|
||||
Reference in new issue
Block a user