- 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>
176 lines
10 KiB
Python
176 lines
10 KiB
Python
"""Saejae-1 P1 coping + bearing-seat rebar (G4: C1~C20, S1~S2) -> centreline polylines for the generic builder rebar_polylines.py.
|
||
|
||
Inputs: the drawing (DXF 구조도(1) front: stirrup stations, side-loop lines, C18 / C19 / C20 / C7 lines and points; 구조도(2) sections A-A / B-B:
|
||
stirrup polygons, loop / tie y), the schedule, params.json (pedestals), the Okseong rules (H1-H6 style) applied again 2026-10-15.
|
||
Output: 판독/g4_bars.json {"bars": [...], "summary": {...}} used by add_g4.py. Model axes: X along the coping, Y across, Z = EL x 1000, pier centre
|
||
at the origin; the plan is rectangular (no shear).
|
||
Front datum: z = drawing y - 1,429.7 (coping part), centre x 225,817.5.
|
||
"""
|
||
import json
|
||
import math
|
||
from collections import defaultdict
|
||
from pathlib import Path
|
||
|
||
from dxftools import entities
|
||
|
||
HERE = Path(__file__).resolve().parent
|
||
APP = HERE.parents[1] / "Code" / "교각모델링앱"
|
||
P = json.loads((APP / "data" / "새재1교P1" / "params.json").read_text(encoding="utf-8"))
|
||
SCH = {m: float(v) for m, v in json.loads((HERE / "판독" / "g4_schedule.json").read_text(encoding="utf-8")).items()} if (HERE / "판독" / "g4_schedule.json").exists() else {}
|
||
FX, ZO = 225817.5, -1429.7
|
||
bars = []
|
||
STEP_B = [-3722.6, -1215.6, 1291.3, 3798.2]
|
||
STEP_Z = [32531.5, 32632.5, 32733.5, 32834.5, 32936.0]
|
||
TAN_S = 1700.0 / 4800.0 # soffit slope
|
||
COS_S = 4800.0 / math.hypot(4800.0, 1700.0)
|
||
KF = 400.0 / 1700.0 # side taper (half width 1,100 -> 1,500)
|
||
Y23 = [-1375.0 + 125 * k for k in range(23)]
|
||
|
||
|
||
def rb(dia):
|
||
return ((2 * dia if dia <= 16 else 3.5 * dia) + dia) / 2
|
||
|
||
|
||
DIA = {"C1": 29, "C2": 29, "C3": 29, "C4": 29, "C5": 29, "C6": 29, "C7": 29, "C8": 25, "C9": 25, "C10": 25, "C11": 25, "C12": 25,
|
||
"C13": 25, "C14": 22, "C15": 22, "C16": 22, "C17": 22, "C18": 19, "C19": 19, "C20": 19, "S1": 13, "S2": 13}
|
||
|
||
|
||
def add(mark, occ, pts, src, r=None):
|
||
length = sum(math.dist(a, b) for a, b in zip(pts, pts[1:]))
|
||
bars.append({"mark": mark, "occ": occ, "pts": [[round(v, 2) for v in p] for p in pts], "r": rb(DIA[mark]) if r is None else r,
|
||
"design_length": round(length, 1), "src": src})
|
||
|
||
|
||
def step_z(x):
|
||
return STEP_Z[sum(1 for b in STEP_B if x >= b)]
|
||
|
||
|
||
TIER_B = [-3600.5, -1100.5, 1399.5, 3899.5] # drawn stirrup top tiers (stations; between 2 stations, not at the step edges)
|
||
|
||
|
||
def tier_z(x):
|
||
return STEP_Z[sum(1 for b in TIER_B if x >= b)]
|
||
|
||
|
||
def soffit(x):
|
||
return 29532.0 if abs(x) <= 1200 else 29532.0 + TAN_S * (abs(x) - 1200)
|
||
|
||
|
||
def y_out(z): # stirrup / loop outer line: 128.4 inside the side taper face
|
||
return min(1375.0, 971.6 + 0.235294 * (z - 29532.0))
|
||
|
||
|
||
# ---- C1~C6 top bars: 3 layers, 23 rows, marks alternate, x +-5,875 -------------------------------------------------------------
|
||
for z, legs, (ma, mb) in ((32407.0, 1087.0, ("C1", "C4")), (32332.0, 100.0, ("C2", "C5")), (32232.0, 100.0, ("C3", "C6"))):
|
||
for i, y in enumerate(Y23):
|
||
add((ma, mb)[i % 2], f"y{y:+.0f}", [(-5875, y, z - legs), (-5875, y, z), (5875, y, z), (5875, y, z - legs)],
|
||
f"정면 선 EL {z / 1000:.3f}, x ±5,875, 다리 {legs:.0f}; 단면 사슬 23줄 @125, 교번 [추론 F3형], 겹이음 j 미모델")
|
||
# ---- C19 / C20: stepped top pieces, 23 rows -----------------------------------------------------------------------------------
|
||
PIECES = [("C20", 32508.0, -3597.6, -845.6, "left"), ("C20", 32609.0, -1090.6, 1661.3, "left"), ("C20", 32710.1, 1416.3, 4168.2, "left"),
|
||
("C19", 32811.1, 3923.2, 5875.0, "both")]
|
||
for mark, z, xa, xb, leg in PIECES:
|
||
for y in Y23:
|
||
if leg == "both":
|
||
p = [(xa, y, z - 370), (xa, y, z), (xb, y, z), (xb, y, z - 370)]
|
||
else:
|
||
p = [(xa, y, z - 370), (xa, y, z), (xb, y, z)]
|
||
add(mark, f"x{xa:+.0f}y{y:+.0f}", p, f"정면 선 EL {z / 1000:.4f}, x {xa} ~ {xb}, 다리 370 (단 상면 − 125)")
|
||
# ---- C7: soffit bar ('a' 525 up, slope, flat); 8 bars flat, 2 + 2 raised along the side face (as Okseong H3) ---------------------
|
||
SLOPE = (31320.3 - 29657.0) / (5875.0 - 1178.5)
|
||
CAP = 32400.0
|
||
for y in [-1375.0 + 250 * k for k in range(12)]:
|
||
dz = 0.0 if abs(y) <= 875 else (30184.0 - 29657.0 if abs(y) <= 1125 else 31246.5 - 29657.0)
|
||
zr, top = 31320.3 + dz, 31320.3 + 525 + dz
|
||
if top > CAP:
|
||
xe = 1178.5 + (CAP - (29657.0 + dz)) / SLOPE
|
||
pts = [(-xe, y, CAP), (-1178.5, y, 29657.0 + dz), (1178.5, y, 29657.0 + dz), (xe, y, CAP)]
|
||
else:
|
||
pts = [(-5875, y, top), (-5875, y, zr), (-1178.5, y, 29657.0 + dz), (1178.5, y, 29657.0 + dz), (5875, y, zr), (5875, y, top)]
|
||
add("C7", f"y{y:+.0f}", pts, "정면 하면 경사선(±1,178.5 평평 EL 29.657, ±5,875 EL 31.3203, 위 끝 연직 525), 단면 B-B 점 EL 29.657 / 30.184 / 31.2465")
|
||
# ---- stirrups C8~C13: 94 stations x = -5788 + 125 k; per station 2 rectangles (C8/C10/C12) + 2 pentagons (C9/C11/C13) -------------
|
||
ST = [-5788.0 + 125 * k for k in range(94)]
|
||
OFF = {"PL": -37.5, "R1": -12.5, "R2": 12.5, "PR": 37.5}
|
||
|
||
|
||
def zone(x):
|
||
return 0 if x < -1200 else (1 if x <= 1200 else 2)
|
||
|
||
|
||
for x in ST:
|
||
zt = tier_z(x) - 102.0 # drawn line = pad - 124.5, 22.5 drawn offset -> centre pad - 102
|
||
def zb_at(xb):
|
||
return soffit(xb) + 102.5 / (1.0 if abs(xb) <= 1200 else COS_S)
|
||
rect, pent = (("C8", "C9"), ("C10", "C11"), ("C12", "C13"))[zone(x)]
|
||
for tag, (y0, y1) in (("R1", (-875.0, 625.0)), ("R2", (-625.0, 875.0))):
|
||
xx = x + OFF[tag]
|
||
zb = zb_at(xx)
|
||
add(rect, f"x{x:+.0f}{tag}", [(xx, (y0 + y1) / 2, zt), (xx, y1, zt), (xx, y1, zb), (xx, y0, zb), (xx, y0, zt), (xx, (y0 + y1) / 2, zt)],
|
||
f"정면 연직선 x {x}, 단면 A-A/B-B 직사각 y {y0:.0f} ~ {y1:.0f}, 위 = 단 상면 − 102, 아래 = 하면 + 102.5 [x 어긋남 {OFF[tag]:+.1f}, 추론]")
|
||
for tag, s in (("PL", -1), ("PR", 1)):
|
||
xx = x + OFF[tag]
|
||
zb = zb_at(xx)
|
||
yb = y_out(zb)
|
||
zc = 29532.0 + (1375.0 - 971.6) / 0.235294
|
||
yin = -s * 375.0
|
||
p = [(xx, s * 500.0, zt), (xx, s * 1375.0, zt)]
|
||
if zb < zc - 20:
|
||
p += [(xx, s * 1375.0, zc)]
|
||
p += [(xx, s * yb, zb)]
|
||
else:
|
||
p += [(xx, s * 1375.0, zb)]
|
||
p += [(xx, yin, zb), (xx, yin, zt), (xx, s * 500.0, zt)]
|
||
add(pent, f"x{x:+.0f}{tag}", p, f"단면 B-B 5각형: 안쪽 y {yin:+.0f}, 바깥 ±1,375 → 측면 테이퍼 따라 모따기, [x 어긋남 {OFF[tag]:+.1f}, 추론]")
|
||
# ---- side loops C16 (upper 4) / C14 (lower 7) and ties C17 / C15 (6 rows: y +-352.5, +-602.5, +-852.5) -------------------------------
|
||
D_H = {}
|
||
for e in entities("D1", FX - 6600, 30800, FX + 6600, 34300, "CS-RBAR"):
|
||
if e.dxftype() == "LINE" and abs(e.dxf.start.y - e.dxf.end.y) < 0.5:
|
||
z = round(e.dxf.start.y + ZO, 1)
|
||
D_H[z] = max(D_H.get(z, 0), round(max(abs(e.dxf.start.x - FX), abs(e.dxf.end.x - FX)), 1))
|
||
UP = [32182.0, 31932.0, 31682.0, 31432.0]
|
||
LOW = [31182.0, 30932.0, 30682.0, 30432.0, 30182.0, 29932.0, 29682.0]
|
||
for levels, loop, tie in ((UP, "C16", "C17"), (LOW, "C14", "C15")):
|
||
for z in levels:
|
||
xe = D_H[z]
|
||
yo = 1352.5 if z >= 31400 else y_out(z) - 0.0
|
||
if z < 31400:
|
||
yo = round(1337.3 - (31182.0 - z) * 0.2354, 1) # drawn dots ±1337.3 ... ±984.3 (taper)
|
||
add(loop, f"z{z:.0f}", [(0, yo, z), (xe, yo, z), (xe, -yo, z), (-xe, -yo, z), (-xe, yo, z), (0, yo, z)],
|
||
f"정면 선 EL {z / 1000:.3f} x ±{xe}, 단면 B-B 점 y ±{yo}")
|
||
for y in (-852.5, -602.5, -352.5, 352.5, 602.5, 852.5):
|
||
add(tie, f"z{z:.0f}y{y:+.1f}", [(-xe, y, z), (xe, y, z)], f"정면 선 EL {z / 1000:.3f}, 단면 점 y {y} (135° 갈고리 미모델)")
|
||
# ---- C18: Y-direction U at every drawn front point (7 levels, 92) ------------------------------------------------------------------
|
||
dots = defaultdict(list)
|
||
for e in entities("D1", FX - 6600, 30800, FX + 6600, 34300, "CS-RBAR"):
|
||
if e.dxftype() == "INSERT":
|
||
dots[round(e.dxf.insert.y + ZO, 1)].append(round(e.dxf.insert.x - FX, 1))
|
||
C18_Z = [32457.0, 32432.0, 32309.5, 32209.5, 31682.0, 30682.0, 29682.0]
|
||
for z in C18_Z:
|
||
hl = y_out(z)
|
||
for x in sorted(dots[z]):
|
||
if z >= 32000:
|
||
p = [(x, -hl, z - 370), (x, -hl, z), (x, hl, z), (x, hl, z - 370)]
|
||
else:
|
||
yl = y_out(z + 370)
|
||
p = [(x, -yl, z + 370), (x, -hl, z), (x, hl, z), (x, yl, z + 370)]
|
||
add("C18", f"x{x:+.0f}z{z:.0f}", p, f"정면 점 EL {z / 1000:.4f}, 측면 폭 ±{hl:.1f}, 다리 370 (낮은 단은 위로, 옥성교 H3 규칙)")
|
||
# ---- seat bars per pedestal: S1 (11, a 2,297 along Y), S2 (20, a 1,200 along X), legs 700 down -------------------------------------
|
||
for k, ped in enumerate(P["bearing_seats"]["pedestals"]):
|
||
xs = [c[0] for c in ped["corners"]]
|
||
ys = [c[1] for c in ped["corners"]]
|
||
cx, cy = (min(xs) + max(xs)) / 2, (min(ys) + max(ys)) / 2
|
||
zp = (ped["top"]["z0"] + ped["top"]["z1"]) / 2
|
||
z1, z2 = zp - 50, zp - 50 - 13
|
||
for j, dx in enumerate([-600.0] + [-500.0 + 125 * i for i in range(9)] + [600.0]):
|
||
add("S1", f"P{k + 1}-{j:02d}", [(cx + dx, cy - 1148.5, z1 - 700), (cx + dx, cy - 1148.5, z1), (cx + dx, cy + 1148.5, z1), (cx + dx, cy + 1148.5, z1 - 700)],
|
||
f"교좌면 보강 상세, 대좌 {k + 1}, S1 11 × 대좌 [추론 방향]", r=rb(13))
|
||
for j, dy in enumerate([-1148.5] + [-1062.5 + 125 * i for i in range(18)] + [1148.5]):
|
||
add("S2", f"P{k + 1}-{j:02d}", [(cx - 600, cy + dy, z2 - 700), (cx - 600, cy + dy, z2), (cx + 600, cy + dy, z2), (cx + 600, cy + dy, z2 - 700)],
|
||
f"교좌면 보강 상세, 대좌 {k + 1}, S2 20 × 대좌 [추론 방향]", r=rb(13))
|
||
|
||
count = {}
|
||
for b in bars:
|
||
count[b["mark"]] = count.get(b["mark"], 0) + 1
|
||
out = {"bars": bars, "summary": {"bars": len(bars), "per_mark": count}}
|
||
(HERE / "판독" / "g4_bars.json").write_text(json.dumps(out, ensure_ascii=False), encoding="utf-8")
|
||
print(json.dumps(out["summary"], ensure_ascii=False))
|