"""G3 gate checker: footing rebar B1-B12 (check C9) and rebar colours inside Rhino 8. Globals: __rhino_doc__, EXPECTED_PATH, RESULT_PATH. Expected values from EXPECTED_PATH sections G1 (footing bottom) and G3 (D3 schedule, D1/D2 dimension chains, F1 layer heights); mid-layer positions are drawn dots (derived, not independent). Generator params are not read. Ported from Code/교각모델링/verify_footing_rebar.py (same checks and order). """ import json import math import os import sys import Rhino.Geometry as rg from Rhino.DocObjects import ObjectColorSource sys.path.insert(0, os.path.dirname(__file__)) from _common import all_objects, load_expected, row, series, write_result # noqa: E402 doc = __rhino_doc__ X = load_expected(EXPECTED_PATH) PID, G = X["pier_id"], X["G3"] FB = X["G1"]["footing"]["z0"] SCHED = {m: tuple(v) for m, v in G["schedule"].items()} DIA = G["dia"] Y_BOT, Y_250, X_BOT, X_250 = (row(G["rows"][k]) for k in ("Y_BOT", "Y_250", "X_BOT", "X_250")) Z = G["layer_z"] B10_Z = series(G["b10_levels"]) B9_X, B9_Y = series(G["b9_grid"]["x"]), series(G["b9_grid"]["y"]) PALETTE = {int(k): tuple(v) for k, v in X["rebar_palette"].items()} results = [] def check(item, exp, act, tol=1.0): 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 objs(prefix, mark): return [o for o in all_objects(doc) if (o.Attributes.GetUserString("id") or "").startswith(prefix) and o.Attributes.GetUserString("mark") == mark] def main(): cl = {m: objs(f"{PID}-RC-", m) for m in SCHED} pp = {m: objs(f"{PID}-R-", m) for m in SCHED} 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])}") if cl[m]: worst = max(abs(float(o.Attributes.GetUserString("design_length")) - L) for o in cl[m]) check(f"{m} original length vs L (worst)", 0, worst) dev = [o.Geometry.GetLength() - L for o in cl[m]] info(f"{m} curve length - L (bend fillets)", f"min {min(dev):.1f} / max {max(dev):.1f}") def mids(m): return [o.Geometry.PointAt(o.Geometry.Domain.Mid) for o in cl[m]] for m in ("B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8"): ps = mids(m) check(f"{m} layer z (worst)", 0, max(abs(p.Z - (FB + Z[m])) for p in ps)) def pos_check(marks, axis, expected, name): got = sorted(round(getattr(p, axis), 1) for m in marks for p in mids(m)) ok = len(got) == len(expected) worst = max(abs(a - b) for a, b in zip(got, expected)) if ok else 1e9 check(f"{name} positions vs chain (worst, n={len(got)}/{len(expected)})", 0, worst) pos_check(("B2", "B4"), "X", Y_BOT, "bottom Y-bars B2+B4") pos_check(("B6", "B8"), "Y", X_BOT, "bottom X-bars B6+B8") pos_check(("B3",), "X", Y_250, "B3") pos_check(("B1",), "X", Y_250, "B1") pos_check(("B7",), "Y", X_250, "B7") pos_check(("B5",), "Y", X_250, "B5") ye, xe = G["rows"]["Y_BOT"], G["rows"]["X_BOT"] b2x = sorted(round(p.X) for p in mids("B2")) flag(f"B2 at ends and alternate from {ye['start']}", b2x == sorted(list(ye["ends"]) + Y_BOT[1:-1][0::2]), "") b6y = sorted(round(p.Y) for p in mids("B6")) flag(f"B6 at ends and alternate from {xe['start']}", b6y == sorted(list(xe["ends"]) + X_BOT[1:-1][0::2]), "") mid = G["mid_levels"] for m in ("B11", "B12"): zs = sorted({round(p.Z - FB) for p in mids(m)}) flag(f"{m} mid levels {'/'.join(str(v) for v in mid)} [derived]", zs == mid, str(zs)) zs = sorted(round(o.Geometry.GetBoundingBox(True).Min.Z - FB, 1) for o in cl["B10"]) check("B10 levels vs chain (worst)", 0, max(abs(a - b) for a, b in zip(zs, B10_Z)) if len(zs) == len(B10_Z) else 1e9) bb = cl["B10"][0].Geometry.GetBoundingBox(True) check("B10 centreline size X", G["b10_size"][0], bb.Max.X - bb.Min.X) check("B10 centreline size Y", G["b10_size"][1], bb.Max.Y - bb.Min.Y) # B9: vertical leg x = bbox min X (hooks point +X); y constant pts = {(round(o.Geometry.GetBoundingBox(True).Min.X), round(o.Geometry.GetBoundingBox(True).Center.Y)) for o in cl["B9"]} exp = {(x, y) for i, x in enumerate(B9_X) for j, y in enumerate(B9_Y) if (i + j) % 2 == 0} flag(f"B9 positions = checkerboard of {len(B9_X)}x{len(B9_Y)} grid", pts == exp, f"{len(pts)} / {len(exp)}") zr = [o.Geometry.GetBoundingBox(True) for o in cl["B9"]] e0, e1 = G["b9_erec"] a9, c9 = G["b9_a"], G["b9_c"] check("B9 bottom z (worst of all)", 0, max(abs(b.Min.Z - (FB + (e0 + e1) / 2 - a9 / 2)) for b in zr)) check("B9 vertical extent a (bottom leg z to top of straight)", a9, max(b.Max.Z for b in zr[:1]) - min(b.Min.Z for b in zr[:1]) - c9 / math.pi, tol=1.0) solids = [] for oid in (f"{PID}-FT", f"{PID}-CL", f"{PID}-CP"): o = [x for x in all_objects(doc) if x.Attributes.GetUserString("id") == oid][0] solids.append(o.Geometry) ft = solids[0] shell = None for order in ([solids[1], solids[0], solids[2]], solids): # union is order-sensitive in Rhino 8 u = rg.Brep.CreateBooleanUnion(order, 0.01) if u and len(u) == 1: shell = u[0] break if shell is None: raise RuntimeError("concrete union failed") cover = {} for m in SCHED: bad, cov = 0, 1e9 for o in cl[m]: c = o.Geometry for t in c.DivideByCount(max(4, int(c.GetLength() / 200)), True): p = c.PointAt(t) r = DIA[m] / 2 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 ft.IsPointInside(q, 0.01, False): bad += 1 cov = min(cov, p.DistanceTo(shell.ClosestPoint(p)) - r) cover[m] = round(cov, 1) check(f"{m} sample points outside footing", 0, bad, tol=0) info("min cover to concrete exterior per mark", json.dumps(cover)) wrong = 0 for o in all_objects(doc): if (o.Attributes.GetUserString("id") or "").startswith(f"{PID}-R-"): d = int(o.Attributes.GetUserString("dia")[1:]) c = o.Attributes.ObjectColor if o.Attributes.ColorSource != ObjectColorSource.ColorFromObject or (c.R, c.G, c.B) != PALETTE[d]: wrong += 1 check("rebar pipe colour by diameter mismatches (all rebar)", 0, wrong, tol=0) write_result(RESULT_PATH, results, dict(cover=cover)) main()