Files
Pier_v1/Code/교각모델링앱/engine/rhino/shaft_rebar.py
T
ahnyongjooandClaude Sonnet 5.5 964a841de1 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>
2026-10-28 11:20:18 +09:00

181 lines
8.8 KiB
Python

"""G3 stage, form "shaft cage" (Ramp-A P1, 2026-10-13): drilled-shaft reinforcement cage (no footing). Straight main
bars in two pieces joined by couplers (upper / lower mark), lapped half-ring hoops with 135-deg hooks, X / Y cross
ties, bottom U bars in vertical planes through the centre, and the anti-uplift plate under them.
Run inside Rhino 8 via runner (globals PARAMS_PATH) after the G1 / G2 builders. Units mm, Z = EL x 1000.
params shaft_rebar:
main {count, start_deg, radius, pieces: [{mark, z0, z1}, ...] bottom to top, coupler_length}
hoops {mark, radius, levels, a, lap, hook, join_deg [first, second]} joins alternate per level (S5)
ties {mark, levels, straight, leg, hook_tail, y_lift} as column_rebar_straight.py (S5)
u_bars {mark, count, z_bottom, half_bottom, half_top, leg_top_z, top_z, bend_r} one U per plane at main-bar angle
i (i < count): bottom +-half_bottom at z_bottom, sloped legs to +-half_top at leg_top_z, vertical to top_z (S3)
plate {size, thickness, z_top, colour} square, centred (S4)
design_lengths {mark: schedule-style original length per piece}
Bars below the pile segment boundary are hosted by the lower pile segment, above it by the upper one.
"""
import json
import math
import Rhino
import Rhino.Geometry as rg
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"]
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"])
SEGS = P["pile"]["segments"]
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 host(z):
for i, s in enumerate(SEGS, 1):
if s["z0"] <= z <= s["z1"]:
return f"{PID}-PL{i:02d}"
return ""
def add_bar(curve, mark, occ, design_len, 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")
bb = curve.GetBoundingBox(True)
common = dict(mark=mark, dia=dia, shape=shape, occurrence=occ, host=host((bb.Min.Z + bb.Max.Z) / 2),
anchor_hosts="", 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}", (110, 110, 110)), 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):
"""As column_rebar_straight.py: straight part on the X / Y diameter, 90-deg leg down at one end, 135-deg hook."""
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}-PT",)) or (oid.startswith((f"{PID}-R-", f"{PID}-RC-"))
and (o.Attributes.GetUserString("mark") or "") in SR_MARKS):
doc.Objects.Delete(o, True)
DL = SR["design_lengths"]
m, n = SR["main"], 0
for i in range(m["count"]):
th = m["start_deg"] + i * 360.0 / m["count"]
for pc in m["pieces"]:
c = rg.LineCurve(pt(m["radius"], th, pc["z0"]), pt(m["radius"], th, pc["z1"]))
add_bar(c, pc["mark"], f"{i + 1:02d}", DL[pc["mark"]],
f"angle {th:.4f} deg; D4 단면 G-G·J-J 61개; r {m['radius']} (S2); EL {pc['z0'] / 1000:.3f}~{pc['z1'] / 1000:.3f}; "
f"커플러 이음 (S6)", coupler=m["coupler_length"])
n += 1
h = SR["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"]],
f"EL {z / 1000:.3f}; r {r} (S1); 겹이음 {h['lap']:.0f} at {a0}/{a0 + 180} deg (S5 [추론])")
n += 1
t = SR["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"]],
f"EL {zt / 1000:.3f}; {name} 방향 (S5 [추론])")
n += 1
u = SR["u_bars"]
for i in range(u["count"]):
th = m["start_deg"] + i * 360.0 / m["count"]
e = rg.Vector3d(math.cos(math.radians(th)), math.sin(math.radians(th)), 0)
P0 = lambda s, z: rg.Point3d(0, 0, z) + e * s
pl = rg.PolylineCurve([P0(-u["half_top"], u["top_z"]), P0(-u["half_top"], u["leg_top_z"]),
P0(-u["half_bottom"], u["z_bottom"]), P0(u["half_bottom"], u["z_bottom"]),
P0(u["half_top"], u["leg_top_z"]), P0(u["half_top"], u["top_z"])])
c = rg.Curve.CreateFilletCornersCurve(pl, u["bend_r"], TOL, math.radians(1)) or pl
add_bar(c, u["mark"], f"{i + 1:02d}", DL[u["mark"]],
f"plane {th:.4f} deg through the centre (S3 [추론]); bottom EL {u['z_bottom'] / 1000:.3f}; c {u['top_z'] - u['leg_top_z']:.0f} welded to P1-2")
n += 1
pt_ = SR["plate"]
a = pt_["size"] / 2
box = rg.Box(rg.BoundingBox(-a, -a, pt_["z_top"] - pt_["thickness"], a, a, pt_["z_top"])).ToBrep()
attr = ObjectAttributes()
attr.LayerIndex = layer(f"{PID}::철근::말뚝::PLATE", tuple(pt_["colour"]))
attr.ObjectColor = Color.FromArgb(*pt_["colour"])
attr.ColorSource = ObjectColorSource.ColorFromObject
oid = f"{PID}-PT01"
attr.Name = oid
for k, v in (("id", oid), ("role", "anti-uplift plate"), ("host", host(pt_["z_top"])),
("src", f"D4 떠오름 방지 PLATE {pt_['size']:.0f}x{pt_['size']:.0f}x{pt_['thickness']:.0f}; U형 바닥 아래 (S4 [추론])")):
attr.SetUserString(k, v)
if doc.Objects.AddBrep(box, attr) == __import__("System").Guid.Empty:
raise RuntimeError("plate: add failed")
doc.Views.Redraw()
print(json.dumps({"bars": n, "plate": 1}))
SR = P["shaft_rebar"]
SR_MARKS = {pc["mark"] for pc in SR["main"]["pieces"]} | {SR[k]["mark"] for k in ("hoops", "ties", "u_bars")}
main()