"""G2 stage, form "straight" (Ramp-A P1, 2026-10-13): column on a drilled shaft, no footing. Straight main bars in one ring (bottom coupler to the shaft cage, not modelled as a solid), lapped half-ring hoops with 135-deg hooks, cross ties in X and Y (90-deg leg at one end, 135-deg hook at the other), from params `column_rebar`. Run inside Rhino 8 via runner (globals PARAMS_PATH) after the G1 builder. Units mm, Z = EL x 1000. params column_rebar: main {mark, count, start_deg, radius, z0, z1, coupler_length} hoops {mark, radius, levels, a, lap, hook, join_deg [first, second]} joins alternate per level (K4) ties {mark, levels, straight, leg, hook_tail, y_lift} X tie at the level, Y tie y_lift above (K3); the 90-deg leg (down) and the 135-deg hook swap ends per level (K4) design lengths {mark: schedule-style original length per piece} Hosts for cover / anchorage: below the column bottom = pile top segment, above the column top = coping. """ 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 PID = P["pier_id"] MARK = {m: (v["dia"], v["shape"]) for m, v in P["rebar_marks"].items()} DIA = {d: float(d[1:]) for d, _ in MARK.values()} CL_RGB = tuple(P["display"]["centreline_colour"]) COLORS = {m: tuple(c) for m, c in P["display"].get("column_mark_colours", {}).items()} Z_CL0, Z_CL1 = P["column"]["z0"], P["column"]["z1"] PILE_TOP = f"{PID}-PL{len(P['pile']['segments']):02d}" 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_bend_radius(dia_name): d = DIA[dia_name] return ((2 * d if d <= 16 else 3.5 * d) + d) / 2 def hosts(z): return PILE_TOP if z < Z_CL0 else (f"{PID}-CP" if z > Z_CL1 else "") def add_bar(curve, mark, occ, design_len, anchor, src, coupler=0.0): 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=f"{PID}-CL", anchor_hosts=anchor, design_length=f"{design_len:.3f}", coupler_length=f"{coupler:.3f}", src=src) for geo, lay, prefix in ((curve, layer(f"{PID}::철근::중심선", CL_RGB), f"{PID}-RC"), (pipes[0], layer(f"{PID}::철근::기둥::{mark}", COLORS.get(mark, (200, 140, 90))), f"{PID}-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) gid = doc.Objects.AddBrep(geo, attr) if isinstance(geo, rg.Brep) else doc.Objects.AddCurve(geo, attr) if gid == __import__("System").Guid.Empty: raise RuntimeError(f"{oid}: add failed") def pt(r, deg, z): t = math.radians(deg) return rg.Point3d(r * math.cos(t), r * math.sin(t), z) def hooked_arc(r, a0, a1, z, hook, bend_r): """Arc a0 -> a1 (deg, ccw) with inward 135-deg hooks at both ends, fillet bend_r (as column_rebar_ring.py).""" arc = rg.ArcCurve(rg.Arc(pt(r, a0, z), pt(r, (a0 + a1) / 2, z), pt(r, a1, z))) pieces = [] for at_start in (True, False): p = arc.PointAtStart if at_start else arc.PointAtEnd t = -arc.TangentAtStart if at_start else arc.TangentAtEnd n = rg.Vector3d(-p.X, -p.Y, 0) n.Unitize() d = t * math.cos(math.radians(135)) + n * math.sin(math.radians(135)) d.Unitize() pieces.append(rg.LineCurve(p, p + d * hook)) joined = rg.Curve.JoinCurves([pieces[0], arc, pieces[1]], TOL)[0] return rg.Curve.CreateFilletCornersCurve(joined, bend_r, TOL, math.radians(1)) or joined def tie_curve(axis, z, half, leg, tail, flip): """Straight part on the X (axis 0) or Y (axis 1) diameter at z; 90-deg leg `leg` down at one end, 135-deg hook (tail pointing down and back inwards) at the other; `flip` swaps the ends.""" u = rg.Vector3d(1, 0, 0) if axis == 0 else rg.Vector3d(0, 1, 0) s = -1 if flip else 1 a, b = rg.Point3d(0, 0, z) - u * (s * half), rg.Point3d(0, 0, z) + u * (s * half) d = -u * s * math.cos(math.radians(45)) + rg.Vector3d(0, 0, -1) * math.sin(math.radians(45)) return rg.PolylineCurve([a + rg.Vector3d(0, 0, -leg), a, b, b + d * tail]) def main(): for o in list(doc.Objects): oid = o.Attributes.GetUserString("id") or "" if oid.startswith((f"{PID}-R-P", f"{PID}-RC-P")): doc.Objects.Delete(o, True) cr = P["column_rebar"] DL = cr["design_lengths"] m = cr["main"] n = 0 for i in range(m["count"]): th = m["start_deg"] + i * 360.0 / m["count"] c = rg.LineCurve(pt(m["radius"], th, m["z0"]), pt(m["radius"], th, m["z1"])) add_bar(c, m["mark"], f"{i + 1:02d}", DL[m["mark"]], f"{PILE_TOP},{PID}-CP", f"angle {th:.4f} deg; D2 단면 C-C DIVISION {m['count']} r {m['radius']:.0f}; 정면 EL {m['z0'] / 1000:.3f}" f"~{m['z1'] / 1000:.3f}; 하단 커플러 {m['coupler_length']:.0f} (K1)", coupler=m["coupler_length"]) n += 1 h = cr["hoops"] r, ext = h["radius"], math.degrees(h["lap"] / 2 / h["radius"]) for j, z in enumerate(h["levels"]): a0 = h["join_deg"][j % 2] for q, s in enumerate((a0, a0 + 180)): c = hooked_arc(r, s - ext, s + 180 + ext, z, h["hook"], min_bend_radius(MARK[h["mark"]][0])) add_bar(c, h["mark"], f"L{j + 1:03d}{'ab'[q]}", DL[h["mark"]], hosts(z), f"EL {z / 1000:.3f}; 겹이음 {h['lap']:.0f} at {a0}/{a0 + 180} deg (K4 [추론])") n += 1 t = cr["ties"] rb = min_bend_radius(MARK[t["mark"]][0]) for j, z in enumerate(t["levels"]): for axis, name in ((0, "X"), (1, "Y")): zt = z + (t["y_lift"] if axis == 1 else 0) pl = tie_curve(axis, zt, t["straight"] / 2, t["leg"], t["hook_tail"], flip=(j % 2 == 1)) c = rg.Curve.CreateFilletCornersCurve(pl, rb, TOL, math.radians(1)) or pl add_bar(c, t["mark"], f"L{j + 1:02d}{name}", DL[t["mark"]], hosts(zt), f"EL {zt / 1000:.3f}; {name} 방향{' +' + str(t['y_lift']) + ' (K3 [추론])' if axis else ''}; " f"90° 다리·135° 갈고리 {'좌우 교번' if j % 2 else '기본'} (K4 [추론])") n += 1 doc.Views.Redraw() print(json.dumps({"bars": n})) main()