"""Stage 4c: coping rebar 1st batch C1-C11 (centreline + pipe) from params.json `coping_rebar`. Run inside Rhino 8 after the other build scripts, then apply_rebar_colors.py. Rules: 설계서 v0.1.07 §7. Units mm, X transverse, Y longitudinal, Z = EL x 1000. """ import json import math import Rhino import Rhino.Geometry as rg from Rhino.DocObjects import ObjectAttributes from System.Drawing import Color P = json.load(open(PARAMS_PATH, encoding="utf-8")) doc = __rhino_doc__ TOL = 0.01 DIA = {"H29": 29.0, "H25": 25.0, "H19": 19.0} MARK = {"C1": ("H29", "VIII"), "C2": ("H29", "VIII"), "C3": ("H29", "VIII"), "C4": ("H29", "VIII"), "C5": ("H29", "VIII"), "C6": ("H29", "VIII"), "C7": ("H29", "VII"), "C8": ("H29", "VII"), "C9": ("H25", "IV"), "C10": ("H25", "IV"), "C11": ("H25", "IV"), "C12": ("H25", "X"), "C13": ("H25", "X"), "C14": ("H19", "III"), "C15": ("H19", "III"), "C16": ("H19", "II"), "S1": ("H19", "III"), "S2": ("H19", "III")} ARC_C = (6951.9, 29934.6) # soffit arc centre (right); left is mirrored # coping end faces (concrete): left (-6200, 41911) -> (-6000, 43411); right (6200, 41911) -> (6000, 43698) def xl(z): return -6000 - 200 * (43411 - z) / 1500 def xr(z): return 6000 + 200 * (43698 - z) / 1787 def unit(dx, dz): n = math.hypot(dx, dz) return dx / n, dz / n U_LEFT_DOWN, U_RIGHT_DOWN = unit(-200, -1500), unit(200, -1787) def layer(path, color): parent = -1 parts = path.split("::") for i in range(1, len(parts) + 1): idx = doc.Layers.FindByFullPath("::".join(parts[:i]), -1) if idx < 0: lay = Rhino.DocObjects.Layer() lay.Name, lay.Color = parts[i - 1], Color.FromArgb(*color) if parent >= 0: lay.ParentLayerId = doc.Layers[parent].Id idx = doc.Layers.Add(lay) parent = idx return parent def min_r(dia): d = DIA[dia] return ((2 * d if d <= 16 else 3.5 * d) + d) / 2 # box rule (REBAR_BENDS_20260914) def fillet(curve, dia): f = rg.Curve.CreateFilletCornersCurve(curve, min_r(dia), TOL, math.radians(1)) return f or curve def add_bar(curve, mark, occ, design_len, src, extra=None): dia, shape = MARK[mark] pipes = rg.Brep.CreatePipe(curve, DIA[dia] / 2, False, rg.PipeCapMode.Flat, False, TOL, math.radians(1)) if not pipes or not pipes[0].IsSolid: raise RuntimeError(f"{mark} {occ}: pipe failed") common = dict(mark=mark, dia=dia, shape=shape, occurrence=occ, host="P1-CP", anchor_hosts="", design_length=f"{design_len:.3f}", coupler_length="0.000", src=src) common.update(extra or {}) for geo, lay, prefix in ((curve, layer("P1::철근::중심선", (60, 60, 60)), "P1-RC"), (pipes[0], layer(f"P1::철근::코핑::{mark}", (120, 120, 160)), "P1-R")): attr = ObjectAttributes() attr.LayerIndex = lay oid = f"{prefix}-{mark}-{occ}" attr.Name = oid attr.SetUserString("id", oid) for k, v in common.items(): attr.SetUserString(k, v) (doc.Objects.AddBrep if isinstance(geo, rg.Brep) else doc.Objects.AddCurve)(geo, attr) def soffit_offset_z(x, cp): st = cp["stirrups"] if abs(x) <= cp["bottom"]["half_flat"]: return cp["bottom"]["z_flat"] cx = ARC_C[0] if x > 0 else -ARC_C[0] R = cp["bottom"]["R_offset"] return ARC_C[1] + math.sqrt(R * R - (x - cx) ** 2) def stirrup_top(x, cp): st = cp["stirrups"] if x >= 0: return st["top_right"]["X>=3900"] if x >= 3900 else st["top_right"]["X<3900"] ax = -x t = st["top_left_inferred"] return t["|X|<1296"] if ax < 1296 else (t["1296<=|X|<3801"] if ax < 3801 else t["|X|>=3801"]) def main(): for o in list(doc.Objects): oid = o.Attributes.GetUserString("id") or "" if oid.startswith(("P1-R-C", "P1-RC-C", "P1-R-S", "P1-RC-S")): doc.Objects.Delete(o, True) cp = P["coping_rebar"] ys = cp["y_positions"] n = 0 # top bars (VIII) for tier, t in cp["top"]["tiers"].items(): z, a, (bl, br) = t["z"], t["a"], t["legs"] d = (xr(z) - xl(z) - a) / 2 x0, x1 = xl(z) + d, xr(z) - d for i, y in enumerate(ys): mark = t["marks"][i % 2] pts = [rg.Point3d(x0 + U_LEFT_DOWN[0] * bl, y, z + U_LEFT_DOWN[1] * bl), rg.Point3d(x0, y, z), rg.Point3d(x1, y, z), rg.Point3d(x1 + U_RIGHT_DOWN[0] * br, y, z + U_RIGHT_DOWN[1] * br)] add_bar(fillet(rg.PolylineCurve(pts), "H29"), mark, f"t{tier}y{y:+.0f}", a + bl + br, f"tier {tier} EL {z / 1000:.3f}; end inset {d:.1f}; legs parallel to end faces", {"a_model": f"{x1 - x0:.3f}"}) n += 1 # bottom bars (VII) b = cp["bottom"] R, zf = b["R_offset"], b["z_flat"] hf = ARC_C[0] - math.sqrt(R * R - (zf - ARC_C[1]) ** 2) # exact flat/arc junction (params 1,073.9 is rounded) dth = b["arc_len"] / R for i, y in enumerate(ys): mark = b["marks"][i % 2] pieces = [rg.LineCurve(rg.Point3d(-hf, y, zf), rg.Point3d(hf, y, zf))] for side in (1, -1): cx = side * ARC_C[0] th0 = math.atan2(zf - ARC_C[1], side * hf - cx) th1 = th0 - side * dth thm = (th0 + th1) / 2 p = lambda th: rg.Point3d(cx + R * math.cos(th), y, ARC_C[1] + R * math.sin(th)) arc = rg.ArcCurve(rg.Arc(p(th0), p(thm), p(th1))) up = unit(-200, 1787) if side > 0 else unit(200, 1500) e = p(th1) leg = rg.LineCurve(e, rg.Point3d(e.X + up[0] * b["leg"], y, e.Z + up[1] * b["leg"])) pieces += [arc, leg] joined = rg.Curve.JoinCurves(pieces, TOL) if len(joined) != 1: raise RuntimeError(f"{mark} y{y}: bottom bar did not join ({len(joined)} pieces)") c = joined[0] add_bar(c, mark, f"y{y:+.0f}", 2 * hf + 2 * b["arc_len"] + 2 * b["leg"], "flat EL 40.511 +-1,073.9; arcs R 12,100 concentric with soffit, length 5,273; legs 650 // end faces") n += 1 # stirrups (IV as closed rectangles, widths as drawn in section A-A) st = cp["stirrups"] for mark, xs in (("C10", st["x_c10"]), ("C11", st["x_c11"]), ("C9", st["x_c9"])): for x in xs: zb, zt = soffit_offset_z(x, cp), stirrup_top(x, cp) for k, (y0, y1) in enumerate(st["loops_y"]): ym = (y0 + y1) / 2 pts = [rg.Point3d(x, ym, zb), rg.Point3d(x, y1, zb), rg.Point3d(x, y1, zt), rg.Point3d(x, y0, zt), rg.Point3d(x, y0, zb), rg.Point3d(x, ym, zb)] c = fillet(rg.PolylineCurve(pts), "H25") w, h = y1 - y0, zt - zb b_s = h + st["b_offset"] add_bar(c, mark, f"x{x:+.0f}k{k + 1}", 2 * w + 2 * b_s + 2 * st["c_sched"], f"X {x}; loop {k + 1} Y {y0}..{y1}; laps 2x750 not drawn (length only)", {"b_sched": f"{b_s:.1f}", "w_model": f"{w:.1f}"}) n += 1 # horizontal closed loops C12 (lower 6 levels) / C13 (upper 3 levels), one inside each stirrup lp = cp.get("loops") if lp: R, inset = cp["bottom"]["R_offset"], lp["end_inset_face"] for z, mark in zip(lp["levels"], lp["marks_by_level"]): disc = R * R - (z - ARC_C[1]) ** 2 xs = ARC_C[0] - math.sqrt(disc) if disc > 0 else 1e9 # soffit-offset limit (symmetric) x0, x1 = max(-xs, xl(z) + inset), min(xs, xr(z) - inset) for k, (y0, y1) in enumerate(lp["loops_y"]): xm = (x0 + x1) / 2 pts = [rg.Point3d(xm, y0, z), rg.Point3d(x1, y0, z), rg.Point3d(x1, y1, z), rg.Point3d(x0, y1, z), rg.Point3d(x0, y0, z), rg.Point3d(xm, y0, z)] c = fillet(rg.PolylineCurve(pts), "H25") a_m, w = x1 - x0, y1 - y0 add_bar(c, mark, f"z{z}k{k + 1}", 2 * (a_m + lp["a_offset"]) + 2 * w + 4 * lp["c_sched"], f"EL {z / 1000:.3f}; X {x0:.1f}..{x1:.1f}; loop {k + 1} Y {y0}..{y1}; laps 4x580 length only", {"a_sched": f"{a_m + lp['a_offset']:.1f}", "w_model": f"{w:.1f}"}) n += 1 # C14 transverse (Y) bars with hooks down; C15 / C16 longitudinal (X) bars in the raised step zones if "c14" in cp: c14 = cp["c14"] h, hk = c14["a"] / 2, c14["hook"] up = [tuple(v) for v in c14.get("hooks_up", [])] for x, z in c14["points"]: s = 1 if (x, z) in up else -1 # hooks up only where a down hook would leave the soffit pts = [rg.Point3d(x, -h, z + s * hk), rg.Point3d(x, -h, z), rg.Point3d(x, h, z), rg.Point3d(x, h, z + s * hk)] add_bar(fillet(rg.PolylineCurve(pts), "H19"), "C14", f"x{x:+.0f}z{z:.0f}", c14["a"] + 2 * hk, "D1 정면도 점; hooks " + ("up (HITL fix)" if s > 0 else "down [추론]")) n += 1 c15, c16 = cp["c15"], cp["c16"] for y in c15["y_positions"]: z, (h0, h1) = c15["z"], c15["hooks"] pts = [rg.Point3d(c15["x0"], y, z - h0), rg.Point3d(c15["x0"], y, z), rg.Point3d(c15["x1"], y, z), rg.Point3d(c15["x1"], y, z - h1)] add_bar(fillet(rg.PolylineCurve(pts), "H19"), "C15", f"y{y:+.0f}", c15["x1"] - c15["x0"] + h0 + h1, "D1 정면도 43,598.4 / 3,816~5,900") z = c16["z"] pts = [rg.Point3d(c16["x0"], y, z - c16["hook_left"]), rg.Point3d(c16["x0"], y, z), rg.Point3d(c16["x1"], y, z)] add_bar(fillet(rg.PolylineCurve(pts), "H19"), "C16", f"y{y:+.0f}", c16["x1"] - c16["x0"] + c16["hook_left"], "D1 정면도 43,454.7 / -1,195~4,066; hook at left end [추론]") n += 2 # S1 / S2 bearing-seat reinforcement under each pedestal (rotated with the pedestal) if "s1s2" in cp and "bearing_seats" in P: bs, ss = P["bearing_seats"], cp["s1s2"] corners = bs["pedestal_girder5"]["front_half"] + bs["pedestal_girder5"]["rear_half"] c5 = (sum(p[0] for p in corners) / len(corners), sum(p[1] for p in corners) / len(corners)) t = math.radians(bs["pedestal_girder5"]["rotation_deg"]) ua, uc = (math.sin(t), math.cos(t)), (math.cos(t), -math.sin(t)) # along (2,500) / across (1,200) steps = P["coping"]["top_steps"] for g in range(1, 6): cx_, cy_ = c5[0] + (g - 5) * bs["pitch_x"], c5[1] top = steps[g - 1]["z"] pt = lambda s_al, s_ac, z: rg.Point3d(cx_ + ua[0] * s_al + uc[0] * s_ac, cy_ + ua[1] * s_al + uc[1] * s_ac, z) s1 = ss["s1"] for off in s1["offsets_across"]: z, h = top - s1["z_below_top"], s1["a"] / 2 pts = [pt(-h, off, z - s1["leg"]), pt(-h, off, z), pt(h, off, z), pt(h, off, z - s1["leg"])] add_bar(fillet(rg.PolylineCurve(pts), "H19"), "S1", f"g{g}c{off:+.0f}", s1["a"] + 2 * s1["leg"], f"girder {g} pedestal; upper layer [추론]") n += 1 s2 = ss["s2"] for off in s2["offsets_along"]: z, h = top - s2["z_below_top"], s2["a"] / 2 pts = [pt(off, -h, z - s2["leg"]), pt(off, -h, z), pt(off, h, z), pt(off, h, z - s2["leg"])] add_bar(fillet(rg.PolylineCurve(pts), "H19"), "S2", f"g{g}a{off:+.0f}", s2["a"] + 2 * s2["leg"], f"girder {g} pedestal; lower layer") n += 1 doc.Views.Redraw() print(json.dumps({"coping_bars": n})) main()