- 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>
114 lines
6.7 KiB
Python
114 lines
6.7 KiB
Python
"""Patch (2026-10-14, Okseong P1 G2): optional inner ring / per-zone hoop radius / 90-135 ties in
|
|
engine/rhino/column_rebar_ring.py and the matching optional checks in engine/rhino/checks/g2_ring.py.
|
|
Absent options -> the Ulsan code paths are unchanged. Backups: backup/2026-10-14_okseong_g2/."""
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
APP = Path(__file__).resolve().parents[2]
|
|
BK = APP / "backup" / "2026-10-14_okseong_g2"
|
|
BK.mkdir(parents=True, exist_ok=True)
|
|
B = APP / "engine" / "rhino" / "column_rebar_ring.py"
|
|
C = APP / "engine" / "rhino" / "checks" / "g2_ring.py"
|
|
for f in (B, C):
|
|
if not (BK / f.name).exists():
|
|
shutil.copy2(f, BK / f.name)
|
|
|
|
|
|
def patch(path, reps):
|
|
s = path.read_text(encoding="utf-8")
|
|
for a, b in reps:
|
|
assert s.count(a) == 1, (path.name, a[:60])
|
|
s = s.replace(a, b)
|
|
path.write_text(s, encoding="utf-8")
|
|
|
|
|
|
patch(B, [
|
|
('''includes the coupler).
|
|
"""''',
|
|
'''includes the coupler).
|
|
Optional (Okseong P1, 2026-10-14; absent -> unchanged): main.inner {mark, radius} second ring at the same angles
|
|
with the hook bent inwards; zone.radius per hoop zone; ties.shape "90-135" {leg, tail, y_lift, design_length}:
|
|
straight on the X / Y diameters, 90-deg leg down at one end, 135-deg hook at the other, ends swapped per level,
|
|
the Y tie y_lift above the X tie.
|
|
"""'''),
|
|
(''' f"{PID}-FT,{PID}-CP", f"angle {th:.4f} deg; 배근도(1) 정면도 갈고리 R{R:.0f}·{m['hook_leg']:.0f}; 단면 DIVISION {m['count']}")
|
|
n += 1''',
|
|
''' f"{PID}-FT,{PID}-CP", f"angle {th:.4f} deg; 배근도(1) 정면도 갈고리 R{R:.0f}·{m['hook_leg']:.0f}; 단면 DIVISION {m['count']}")
|
|
n += 1
|
|
if "inner" in m: # second ring, hook bent inwards (Okseong P1)
|
|
ri = m["inner"]["radius"]
|
|
z0 = m["hook_leg_z"]
|
|
leg = rg.LineCurve(pt(ri - R - m["hook_leg"], th, z0), pt(ri - R, th, z0))
|
|
bend = rg.ArcCurve(rg.Arc(pt(ri - R, th, z0),
|
|
pt(ri - R + R * math.sin(math.radians(45)), th, z0 + R - R * math.cos(math.radians(45))),
|
|
pt(ri, th, z0 + R)))
|
|
vert = rg.LineCurve(pt(ri, th, z0 + R), pt(ri, th, top))
|
|
c = rg.Curve.JoinCurves([leg, bend, vert], TOL)[0]
|
|
add_bar(c, m["inner"]["mark"], f"{i + 1:02d}", m["straight_len"] + math.pi / 2 * R + m["hook_leg"],
|
|
f"{PID}-FT,{PID}-CP", f"angle {th:.4f} deg; inner ring r {ri:.0f}, hook inwards")
|
|
n += 1'''),
|
|
(''' mark, typ = zone["mark"], zone["type"]
|
|
for j, z in enumerate(zone["levels"]):''',
|
|
''' mark, typ = zone["mark"], zone["type"]
|
|
r = zone.get("radius", hp["radius"]) # per zone (Okseong P3 1,150 / P4 1,147)
|
|
for j, z in enumerate(zone["levels"]):'''),
|
|
(''' tie = cr["ties"]
|
|
half = tie["straight"] / 2''',
|
|
''' tie = cr["ties"]
|
|
if tie.get("shape") == "90-135": # Okseong P1 (2026-10-14), as column_rebar_straight.py
|
|
rb = min_bend_radius(MARK[tie["mark"]][0])
|
|
for q, z in enumerate(tie["levels"]):
|
|
for axis, name in ((0, "X"), (1, "Y")):
|
|
zt = z + (tie["y_lift"] if axis else 0)
|
|
u = rg.Vector3d(1, 0, 0) if axis == 0 else rg.Vector3d(0, 1, 0)
|
|
s = -1 if q % 2 else 1
|
|
a = rg.Point3d(0, 0, zt) - u * (s * tie["straight"] / 2)
|
|
b = rg.Point3d(0, 0, zt) + u * (s * tie["straight"] / 2)
|
|
d = -u * s * math.cos(math.radians(45)) + rg.Vector3d(0, 0, -1) * math.sin(math.radians(45))
|
|
pl = rg.PolylineCurve([a + rg.Vector3d(0, 0, -tie["leg"]), a, b, b + d * tie["tail"]])
|
|
c = rg.Curve.CreateFilletCornersCurve(pl, rb, TOL, math.radians(1)) or pl
|
|
add_bar(c, tie["mark"], f"L{q + 1:02d}{name}", tie["design_length"], hosts(zt),
|
|
f"EL {zt / 1000:.3f}; {name} (Y +{tie['y_lift']}, 90/135 교번 [추론])")
|
|
n += 1
|
|
doc.Views.Redraw()
|
|
print(json.dumps({"bars": n}))
|
|
return
|
|
half = tie["straight"] / 2'''),
|
|
])
|
|
|
|
patch(C, [
|
|
(''' check(f"{m} radius (mid point)", G["r_hoop"], math.hypot(p.X, p.Y), tol=0.5)''',
|
|
''' check(f"{m} radius (mid point)", G.get("r_hoop_by_mark", {}).get(m, G["r_hoop"]), math.hypot(p.X, p.Y), tol=0.5)
|
|
if "inner" in M: # Okseong P1: inner ring (2026-10-14)
|
|
im = M["inner"]
|
|
tops = [max((o.Geometry.PointAtStart, o.Geometry.PointAtEnd), key=lambda q: q.Z) for o in cl[im["mark"]]]
|
|
lows = [min((o.Geometry.PointAtStart, o.Geometry.PointAtEnd), key=lambda q: q.Z) for o in cl[im["mark"]]]
|
|
check(f"{im['mark']} inner ring radius (worst)", 0, max(abs(math.hypot(q.X, q.Y) - im["radius"]) for q in tops) if tops else 1e9, tol=0.5)
|
|
flag(f"{im['mark']} hooks bent inwards", bool(lows) and all(math.hypot(q.X, q.Y) < im["radius"] for q in lows))'''),
|
|
(''' tz = {}
|
|
for o in cl[tie_mark]:
|
|
tz.setdefault(round(o.Geometry.GetBoundingBox(True).Max.Z, 1), []).append(o)
|
|
lv = sorted(tz)
|
|
flag(f"tie levels {len(TIE_LEVELS)} x {TIE['per_level']}", len(lv) == len(TIE_LEVELS)
|
|
and all(len(v) == TIE["per_level"] for v in tz.values()), f"{len(lv)} levels")
|
|
check(f"tie levels vs chain + {TIE['offset']} (worst)", 0,
|
|
max(abs(a - (b + TIE["offset"])) for a, b in zip(lv, TIE_LEVELS)) if lv else 1e9)''',
|
|
''' if TIE.get("y_lift"): # Okseong P1: X tie at the level, Y tie lifted
|
|
occ_ = lambda o: o.Attributes.GetUserString("occurrence")
|
|
lx = sorted(round(o.Geometry.GetBoundingBox(True).Max.Z, 1) for o in cl[tie_mark] if occ_(o).endswith("X"))
|
|
ly = sorted(round(o.Geometry.GetBoundingBox(True).Max.Z, 1) for o in cl[tie_mark] if occ_(o).endswith("Y"))
|
|
flag(f"tie X / Y levels {len(TIE_LEVELS)} each", len(lx) == len(ly) == len(TIE_LEVELS), f"X {len(lx)} Y {len(ly)}")
|
|
check("tie X levels vs chain (worst)", 0, max(abs(a - b - TIE["offset"]) for a, b in zip(lx, TIE_LEVELS)) if lx else 1e9)
|
|
check(f"tie Y = X + {TIE['y_lift']} (worst)", 0, max(abs(b - a - TIE["y_lift"]) for a, b in zip(lx, ly)) if ly else 1e9)
|
|
else:
|
|
tz = {}
|
|
for o in cl[tie_mark]:
|
|
tz.setdefault(round(o.Geometry.GetBoundingBox(True).Max.Z, 1), []).append(o)
|
|
lv = sorted(tz)
|
|
flag(f"tie levels {len(TIE_LEVELS)} x {TIE['per_level']}", len(lv) == len(TIE_LEVELS)
|
|
and all(len(v) == TIE["per_level"] for v in tz.values()), f"{len(lv)} levels")
|
|
check(f"tie levels vs chain + {TIE['offset']} (worst)", 0,
|
|
max(abs(a - (b + TIE["offset"])) for a, b in zip(lv, TIE_LEVELS)) if lv else 1e9)'''),
|
|
])
|
|
print("patched")
|