"""G5 stage: bridge bearings (black boxes) and sole plates (wedges) on each h2 pad. Run inside Rhino 8 via runner (globals PARAMS_PATH) after concrete.py. Ported from Code/교각모델링/build_bearing_plates_rhino.py (Ungok P1, model v0.1.09). Rules: 설계서 v0.1.10 §8. Reads params.json `bearing_seats`, `sole_plates`, `coping`. Bottom of the bearing = modelled pad top (= coping step + h1 + h2 = EL.'X' within 1 mm). """ import json import math import Rhino import Rhino.Geometry as rg import System from Rhino.DocObjects import ObjectAttributes, ObjectColorSource from System.Drawing import Color P = json.load(open(PARAMS_PATH, encoding="utf-8")) doc = __rhino_doc__ TOL = 0.01 PID = P["pier_id"] H3 = P["sole_plates"].get("h3") # D2 ELEVATION TABLE h3 (common value; a table row may override) 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 corners(cx, cy, along, across, rot_deg): """Plan corners, `along` on +Y rotated clockwise by rot_deg; order: (-a,-c), (+a,-c), (+a,+c), (-a,+c).""" t = math.radians(rot_deg) ua, uc = (math.sin(t), math.cos(t)), (math.cos(t), -math.sin(t)) out = [] for sa, sc in ((-1, -1), (1, -1), (1, 1), (-1, 1)): out.append((cx + sa * ua[0] * along / 2 + sc * uc[0] * across / 2, cy + sa * ua[1] * along / 2 + sc * uc[1] * across / 2, sa * along / 2)) return out def hexa(bottom, top): pts = [rg.Point3d(*p) for p in bottom + top] b = rg.Brep.CreateFromBox(pts) if b is None or not b.IsSolid: raise RuntimeError("hexahedron failed") if b.SolidOrientation == rg.BrepSolidOrientation.Inward: b.Flip() return b def add(brep, key, oid, color, props): attr = ObjectAttributes() attr.LayerIndex = layer(key, color) attr.Name = oid attr.ObjectColor = Color.FromArgb(*color) attr.ColorSource = ObjectColorSource.ColorFromObject attr.SetUserString("id", oid) for k, v in props.items(): attr.SetUserString(k, str(v)) if doc.Objects.AddBrep(brep, attr) == System.Guid.Empty: raise RuntimeError(f"{oid}: AddBrep failed") def main(): for o in list(doc.Objects): if (o.Attributes.GetUserString("id") or "").startswith((f"{PID}-BB", f"{PID}-SP")): doc.Objects.Delete(o, True) sp = P["sole_plates"] if not sp["table"]: # 2026-10-13: an empty table silently produced no bearings raise RuntimeError("sole_plates.table is empty") steps = P["coping"].get("top_steps") bear = {b["no"]: b for b in P["bearing_seats"]["bearings"]} for r in sp["table"]: b = bear[r["no"]] cx, cy = b["centre"] rot = b["rotation_deg"] seat = b["seat_z"] if "seat_z" in b else steps[b["girder_line"] - 1]["z"] # seat_z: tapered form (Ulsan) z0 = seat + b["h1"] + b["h2"] # modelled pad top h3 = r.get("h3", H3) # per bearing when tabled if sp.get("bearing_size") == "AB": # bearing body A (along) x B (across) (Ulsan detail drawing) along, across, rule = b["A"], b["B"], "A x B (교량받침 상세)" else: # (L-100) x (W-100) per user 2026-10-08 (Ungok) along, across, rule = r["L"] - sp["bearing_shrink"], r["W"] - sp["bearing_shrink"], None cb = corners(cx, cy, along, across, rot) add(hexa([(x, y, z0) for x, y, _ in cb], [(x, y, z0 + h3) for x, y, _ in cb]), f"{PID}::교량받침", f"{PID}-BB{r['no']:02d}", tuple(sp["colours"]["bearing"]), dict(role="bridge bearing", no=r["no"], size=f"{along}x{across}x{h3}", el_x=b["el_x"], src=f"ELEVATION TABLE h3, {rule}" if rule else "D2 ELEVATION TABLE h3, (L-100)x(W-100) per user 2026-10-08")) # sole plate: bottom flat, top sloped S% along the pad long axis, centre thickness T (sign of S sets thick end) zb = z0 + h3 cs = corners(cx, cy, r["L"], r["W"], rot) top = [(x, y, zb + r["T"] + r["S"] / 100 * s) for x, y, s in cs] add(hexa([(x, y, zb) for x, y, _ in cs], top), f"{PID}::솔플레이트", f"{PID}-SP{r['no']:02d}", tuple(sp["colours"]["sole_plate"]), dict(role="sole plate", no=r["no"], S=r["S"], L=r["L"], W=r["W"], T=r["T"], end_thick=f"{r['T'] - r['S'] / 100 * r['L'] / 2:.2f}/{r['T'] + r['S'] / 100 * r['L'] / 2:.2f}", src=sp.get("sole_plate_src", "D2 솔플레이트 상세 TABLE; slope along pad axis, thick end -Y [HITL]"))) doc.Views.Redraw() print(json.dumps({"bearings": len(sp["table"]), "sole_plates": len(sp["table"])})) main()