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,185 @@
|
||||
"""G4 gate checker, Ramp-A P1 coping rebar (2026-10-14): C1~C16 and bearing-seat S1~S4 as centreline polylines.
|
||||
|
||||
Globals: __rhino_doc__, EXPECTED_PATH, RESULT_PATH. Expected values (section G4, plus the G1 coping dimensions) come
|
||||
from the D3 schedule (L, N), the D2 / D3 drawn positions and section dimensions, and the user decisions T1-T6;
|
||||
generator params are not read.
|
||||
Checks (C10): counts, fixed-length marks vs L (variable marks recorded), top layers (level, y rows, x ends),
|
||||
stirrup positions per zone, stirrup top / bottom cover along the soffit, inner stirrup offset, side loops per level,
|
||||
C6 bottom level, C15 / C16, seat bars per bearing, containment in the coping + pedestals, min cover (record).
|
||||
"""
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import statistics
|
||||
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, G, C, CO = X["pier_id"], X["G4"], X["G1"]["coping"], X["G1"]["column"]
|
||||
SCHED = {m: tuple(v) for m, v in G["schedule"].items()}
|
||||
ZB = CO["z0"] + CO["length"] # column top = coping bottom
|
||||
ZT = ZB + C["soffit_rise"]
|
||||
ZTL, ZTR = C["z_top_left"], C["z_top_right"]
|
||||
R, HB, HT = C["soffit_radius"], C["column_face_half"], C["bottom_length"] / 2
|
||||
results = []
|
||||
|
||||
|
||||
def soffit_centre():
|
||||
ax, az, bx, bz = -HB, ZB, -HT, ZT # R arc through (-HB, ZB) and (-HT, ZT), centre below the chord
|
||||
mx, mz = (ax + bx) / 2, (az + bz) / 2
|
||||
d = math.hypot(bx - ax, bz - az)
|
||||
h = math.sqrt(R * R - d * d / 4)
|
||||
ux, uz = (bz - az) / d, -(bx - ax) / d
|
||||
return min(((mx + h * ux, mz + h * uz), (mx - h * ux, mz - h * uz)), key=lambda c: c[1])
|
||||
|
||||
|
||||
SCX, SCZ = soffit_centre()
|
||||
SCX = abs(SCX)
|
||||
|
||||
|
||||
def soffit_z(x):
|
||||
return ZB if abs(x) <= HB else SCZ + math.sqrt(R * R - (SCX - abs(x)) ** 2)
|
||||
|
||||
|
||||
def check(item, exp, act, tol=1.0):
|
||||
results.append(dict(check="C10", 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="C10", item=item, detail=detail, result="PASS" if ok else "FAIL"))
|
||||
|
||||
|
||||
def info(item, detail):
|
||||
results.append(dict(check="C10", item=item, detail=detail, result="INFO"))
|
||||
|
||||
|
||||
def row(s):
|
||||
return [s["start"] + s["pitch"] * i for i in range(s["n"])]
|
||||
|
||||
|
||||
def bb(o):
|
||||
return o.Geometry.GetBoundingBox(True)
|
||||
|
||||
|
||||
def worst(pairs):
|
||||
pairs = list(pairs)
|
||||
return max((abs(a - b) for a, b in pairs), default=1e9)
|
||||
|
||||
|
||||
def main():
|
||||
cl = {m: [] for m in SCHED}
|
||||
pp = {m: [] for m in SCHED}
|
||||
for o in all_objects(doc):
|
||||
oid = o.Attributes.GetUserString("id") or ""
|
||||
m = o.Attributes.GetUserString("mark")
|
||||
if m in SCHED and oid.startswith(f"{PID}-RC-"):
|
||||
cl[m].append(o)
|
||||
elif m in SCHED and oid.startswith(f"{PID}-R-"):
|
||||
pp[m].append(o)
|
||||
for m, (L, n) in SCHED.items():
|
||||
check(f"{m} bar count vs schedule", n, len(cl[m]), tol=0)
|
||||
flag(f"{m} pipes closed & paired", len(pp[m]) == len(cl[m]) and all(o.Geometry.IsSolid for o in pp[m]),
|
||||
f"{len(pp[m])}/{len(cl[m])}")
|
||||
dl = [float(o.Attributes.GetUserString("design_length")) for o in cl[m]]
|
||||
if not dl:
|
||||
continue
|
||||
if m in G["fixed_length"]:
|
||||
check(f"{m} original length vs L (worst)", 0, max(abs(v - L) for v in dl))
|
||||
else:
|
||||
info(f"{m} mean original length vs L (variable mark, record)",
|
||||
f"mean {statistics.mean(dl):.1f} / L {L} (diff {statistics.mean(dl) - L:+.1f}); min {min(dl):.0f} max {max(dl):.0f}")
|
||||
# top layers (straight part = top of the bar)
|
||||
for m, s in G["layers"].items():
|
||||
got = sorted(round((bb(o).Min.Y + bb(o).Max.Y) / 2, 1) for o in cl[m])
|
||||
want = row(s["y"])
|
||||
check(f"{m} y rows (worst, {len(got)}/{len(want)})", 0, worst(zip(got, want)) if len(got) == len(want) else 1e9)
|
||||
check(f"{m} level of the straight part (worst)", 0, max(abs(bb(o).Max.Z - s["z"]) for o in cl[m]))
|
||||
if "x_top" in s:
|
||||
x0, x1 = s["x_top"]
|
||||
ends = []
|
||||
for o in cl[m]:
|
||||
c = o.Geometry
|
||||
top_pts = [p for p in (c.PointAt(t) for t in c.DivideByLength(5, True)) if abs(p.Z - s["z"]) < 0.5]
|
||||
ends.append(max(abs(min(p.X for p in top_pts) - x0), abs(max(p.X for p in top_pts) - x1)))
|
||||
check(f"{m} straight part x ends vs drawn (worst; fillet tangent points)", 0, max(ends), tol=s.get("x_tol", 70))
|
||||
# stirrups
|
||||
st = G["stirrups"]
|
||||
xs = row(st["x"])
|
||||
for zone, (m_out, m_in, x0, x1) in st["zones"].items():
|
||||
want = [x for x in xs if x0 <= x <= x1]
|
||||
got_o = sorted(round(bb(o).Min.X, 1) for o in cl[m_out])
|
||||
got_i = sorted(round(bb(o).Min.X, 1) for o in cl[m_in])
|
||||
check(f"{m_out} positions = drawn x ({len(want)})", 0, worst(zip(got_o, want)) if len(got_o) == len(want) else 1e9)
|
||||
check(f"{m_in} positions = drawn x + {st['inner_dx']} (2 per x)", 0,
|
||||
worst(zip(got_i, sorted(x + st["inner_dx"] for x in want for _ in (0, 1)))) if len(got_i) == 2 * len(want) else 1e9)
|
||||
for m in (m_out, m_in):
|
||||
dt, dbm = [], []
|
||||
for o in cl[m]:
|
||||
b = bb(o)
|
||||
x = b.Min.X - (st["inner_dx"] if m == m_in else 0)
|
||||
t = ZTL if x <= 0 else ZTR
|
||||
dt.append(abs((t - b.Max.Z) - st["top_cover"]))
|
||||
dbm.append(abs((b.Min.Z - soffit_z(x)) - st["bottom_cover"] * (1 if abs(x) < HB else
|
||||
# on the arc the 77.5 cover is radial: vertical gap = R' - R projected
|
||||
(SCZ + math.sqrt((R + st["bottom_cover"]) ** 2 - (SCX - abs(x)) ** 2) - soffit_z(x)) / st["bottom_cover"])))
|
||||
check(f"{m} top cover {st['top_cover']} (worst)", 0, max(dt))
|
||||
check(f"{m} bottom cover {st['bottom_cover']} to the soffit (worst)", 0, max(dbm))
|
||||
ys = [(bb(o).Min.Y, bb(o).Max.Y) for o in cl[m_out]]
|
||||
check(f"{m_out} legs y {st['outer_y']} (worst)", 0, worst([(a, st["outer_y"][0]) for a, _ in ys] + [(b, st["outer_y"][1]) for _, b in ys]))
|
||||
# side loops
|
||||
lp = G["loops"]
|
||||
for m, levels in lp["levels"].items():
|
||||
zs = {}
|
||||
for o in cl[m]:
|
||||
zs.setdefault(round(bb(o).Min.Z, 1), []).append(o)
|
||||
flag(f"{m} levels {levels} x {lp['per_level']}", sorted(zs) == sorted(levels)
|
||||
and all(len(v) == lp["per_level"] for v in zs.values()), f"{sorted(zs)}")
|
||||
flat = [abs(bb(o).Max.Z - bb(o).Min.Z) for o in cl[m]]
|
||||
check(f"{m} loops horizontal (worst dz)", 0, max(flat) if flat else 1e9, tol=0.01)
|
||||
# C6, C15, C16
|
||||
c6 = G["c6"]
|
||||
check("C6 bottom straight level (worst)", 0, max(abs(bb(o).Min.Z - c6["z_bottom"]) for o in cl["C6"]) if cl["C6"] else 1e9)
|
||||
got = sorted(round(o.Geometry.PointAt(o.Geometry.Domain.Mid).Y, 1) for o in cl["C6"])
|
||||
check("C6 y rows at mid-span (worst)", 0, worst(zip(got, c6["y"])) if len(got) == len(c6["y"]) else 1e9)
|
||||
for m in ("C15",):
|
||||
w = [bb(o).Max.Y - bb(o).Min.Y for o in cl[m]]
|
||||
check(f"{m} width a (worst)", G["c15"]["a"], max(w, key=lambda v: abs(v - G["c15"]["a"])) if w else 0)
|
||||
# seats
|
||||
for no, want in G["seats"].items():
|
||||
for m, n in want.items():
|
||||
k = sum(1 for o in cl[m] if o.Attributes.GetUserString("occurrence").startswith(f"B{no}-"))
|
||||
check(f"bearing {no} {m} count", n, k, tol=0)
|
||||
# containment and cover
|
||||
solids = [o.Geometry for o in all_objects(doc) if (o.Attributes.GetUserString("id") or "") == f"{PID}-CP"
|
||||
or (o.Attributes.GetUserString("id") or "").startswith(f"{PID}-BS")]
|
||||
solids = [s if isinstance(s, rg.Brep) else rg.Brep.TryConvertBrep(s) for s in solids]
|
||||
cp = [o.Geometry for o in all_objects(doc) if (o.Attributes.GetUserString("id") or "") == f"{PID}-CP"][0]
|
||||
col = [o.Geometry for o in all_objects(doc) if (o.Attributes.GetUserString("id") or "") == f"{PID}-CL"]
|
||||
info("concrete solids for containment", f"{len(solids)} (coping + pedestals); bars below the coping bottom checked in the column")
|
||||
cover = {}
|
||||
for m in SCHED:
|
||||
bad, cov = 0, 1e9
|
||||
r = G["dia"][m] / 2
|
||||
for o in cl[m]:
|
||||
cu = o.Geometry
|
||||
for t in cu.DivideByCount(max(6, int(cu.GetLength() / 150)), True):
|
||||
p = cu.PointAt(t)
|
||||
for q in (p, p + rg.Vector3d(r, 0, 0), p - rg.Vector3d(r, 0, 0), p + rg.Vector3d(0, r, 0),
|
||||
p - rg.Vector3d(0, r, 0), 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 + col):
|
||||
bad += 1
|
||||
if cp.IsPointInside(p, 0.01, False):
|
||||
cov = min(cov, p.DistanceTo(cp.ClosestPoint(p)) - r)
|
||||
cover[m] = round(cov, 1)
|
||||
check(f"{m} sample points outside concrete", 0, bad, tol=0)
|
||||
info("min cover to the coping surface per mark (record; seat bars measured to the coping only)", json.dumps(cover))
|
||||
write_result(RESULT_PATH, results, dict(cover=cover))
|
||||
|
||||
|
||||
main()
|
||||
Reference in new issue
Block a user