- 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>
213 lines
13 KiB
Python
213 lines
13 KiB
Python
"""R1 (shape) reading of the Ungok P2 drawings -> app params / expected (data/운곡교p2).
|
|
|
|
params = drawn CAD entities (D1, read by handle) + D2 ELEVATION TABLE for bearing values (user rule from P1:
|
|
table wins over drawn pedestal/pad sizes).
|
|
expected = dimension texts / EL texts / tables (D1, D2), typed below with their text as the source.
|
|
Drawing frame (same as P1): front view column CL x=12159.4, column top y=16515.6, footing bottom y=8115.6;
|
|
side view CL x=30133.6; plan datum (12162.4, 26412.3).
|
|
Writes only the R1 sections (others stay as they are); previous files go to data/운곡교p2/backup/.
|
|
"""
|
|
import json
|
|
import math
|
|
import shutil
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
APP = HERE.parent / "교각모델링앱"
|
|
PIER = APP / "data" / "운곡교p2"
|
|
sys.path.insert(0, str(HERE))
|
|
sys.path.insert(0, str(APP))
|
|
import dxftools as T # noqa: E402
|
|
from engine import reader # noqa: E402
|
|
|
|
D1 = T.doc("D1").entitydb
|
|
FX, COL_TOP_Y, FT_BOT_Y = 12159.4, 16515.6, 8115.6
|
|
PLAN = (12162.4, 26412.3)
|
|
EL_FT_BOT = 25123 # D1 'EL. 25.123'
|
|
FT_T, COL_H = 2200, 12133 # D1 '2.200', '12.133'
|
|
Z_COL_TOP = EL_FT_BOT + FT_T + COL_H # 39456
|
|
PITCH = 7507 / 3 # D1 평면도 '3@2.502=7.507'
|
|
|
|
|
|
def line(h):
|
|
e = D1[h]
|
|
return (e.dxf.start.x, e.dxf.start.y), (e.dxf.end.x, e.dxf.end.y)
|
|
|
|
|
|
def pts(h):
|
|
return [(p[0], p[1]) for p in D1[h].get_points()]
|
|
|
|
|
|
def rel(p):
|
|
return [round(p[0] - PLAN[0], 1), round(p[1] - PLAN[1], 1)]
|
|
|
|
|
|
def centre(q):
|
|
return [sum(p[0] for p in q) / 4, sum(p[1] for p in q) / 4]
|
|
|
|
|
|
def angle_deg(a, b):
|
|
return math.degrees(math.atan2(b[0] - a[0], b[1] - a[1]))
|
|
|
|
|
|
def z_front(y):
|
|
"""Front-view coping region y -> elevation (mm); coping is drawn to scale above the column top."""
|
|
return Z_COL_TOP + (y - COL_TOP_Y)
|
|
|
|
|
|
def table():
|
|
texts = json.loads((PIER / "drawings" / "texts.json").read_text(encoding="utf-8"))
|
|
tabs = {t["key"]: t for t in reader.table_candidates(texts)}
|
|
el = {int(r["_key"]): r for _, r in reader.table_records(tabs["elevation"]["rows"], str.isdigit)}
|
|
sp = {int(r["_key"]): r for _, r in reader.table_records(tabs["sole_plate"]["rows"], str.isdigit)}
|
|
return el, sp
|
|
|
|
|
|
def params_r1(el):
|
|
ft = line("21A76") # footing bottom 6984.4..17334.4
|
|
lc = line("21B62") # lean bottom 6884.4..17434.4
|
|
side_ft, side_lc = line("21CFC"), line("21DCF")
|
|
steps_x = [line(h)[0][0] - FX for h in ("20E78", "20E7A", "20E7C", "20E7E")] # step risers
|
|
steps_y = [line(h)[1][1] for h in ("20E77", "20E79", "20E7B", "20E7D", "20E7F")] # step treads
|
|
xs = [-6000.0] + [round(x, 1) for x in steps_x] + [6000.0]
|
|
top_steps = [{"x0": xs[i], "x1": xs[i + 1], "z": round(z_front(steps_y[i]), 1)} for i in range(5)]
|
|
tipL, topL = line("20D63") # (6159.4,19515.6) -> (5959.4,18015.6)
|
|
tipR = line("20E74")[0] # (18359.4,18015.6)
|
|
flat = line("20E87") # coping flat bottom 11059.4..13259.4
|
|
p = {
|
|
"lean_concrete": {"size_x": round(lc[0][0] - lc[1][0], 1), "size_y": round(side_lc[0][0] - side_lc[1][0], 1),
|
|
"z_bottom": EL_FT_BOT - 100, "z_top": EL_FT_BOT,
|
|
"src": {"size": "D1 정면도 21B62 / 측면도 21DCF", "thickness": "D1 21B61 (100)"}},
|
|
"footing": {"size_x": round(ft[0][0] - ft[1][0], 1), "size_y": round(side_ft[0][0] - side_ft[1][0], 1),
|
|
"z_bottom": EL_FT_BOT, "thickness": round(line("21A75")[1][1] - line("21A75")[0][1], 1),
|
|
"src": {"size": "D1 정면도 21A76 / 측면도 21CFC", "z_bottom": "D1 'EL. 25.123'",
|
|
"thickness": "D1 21A75"}},
|
|
"column": {"shape": "circular", "diameter": round(line("214C2")[0][0] - line("214C1")[0][0], 1), "height": COL_H,
|
|
"src": {"diameter": "D1 정면도 214C1/214C2", "height": "D1 dim '12.133' (column drawn with break lines)"}},
|
|
"coping": {"length_bottom_tip_to_tip": round(tipR[0] - topL[0], 1), "width_y": round(line("2143A")[1][0] - line("2143A")[0][0], 1),
|
|
"z_bottom_flat": Z_COL_TOP, "flat_bottom_half_width": round((flat[1][0] - flat[0][0]) / 2, 1),
|
|
"soffit_radius": round(D1["20E89"].dxf.radius, 1), "tip_bottom_rise": round(topL[1] - COL_TOP_Y, 1),
|
|
"end_batter_top_inset": round(tipL[0] - topL[0], 1), "top_steps": top_steps,
|
|
"src": {"outline": "D1 정면도 20D63, 20E74, 20E77~20E7F, 20E87, ARC 20E89/20E8B",
|
|
"width_y": "D1 측면도 2143A", "z": "y - 16515.6 + EL 39.456 (column top)"}},
|
|
"piles": {"diameter": 600, "count_x": 6, "count_y": 5,
|
|
"pitch_x": round(line("21C6D")[0][0] - line("21B44")[0][0], 1),
|
|
"pitch_y": round(line("21DD6")[0][0] - line("21DD4")[0][0], 1) * -1,
|
|
"edge": round(line("21B44")[0][0] - ft[1][0], 1), "length": 25900,
|
|
"head_embedment": round(line("21B45")[0][1] - FT_BOT_Y, 1), "kind": "composite pile",
|
|
"src": {"layout": "D1 CS-CNTL 21B44/21C6D/21C88 (X), 21DD2/21DD4/21DD6 (Y)",
|
|
"diameter_length": "D1 '(Φ600.0X9T, L=25.9M)'"}},
|
|
"materials": {"fck": {"lean": "15MPa", "footing": "27MPa", "column": "40MPa", "coping": "40MPa", "seat": "40MPa"},
|
|
"src": "D1 주기 fck=40MPa(기둥·코핑), 27MPa(기초), 정면도 'CON'C fck = 15 MPa'"},
|
|
}
|
|
# bearing seats (pattern of Code/교각모델링/extract_bearing_seats.py)
|
|
(fl, rl), rr, fr = line("20C1E"), line("20C1F")[1], line("20C20")[1]
|
|
sr, sl = line("21A3F") # split line: right point, left point
|
|
ped = [fl, rl, rr, fr]
|
|
front_half, rear_half = [fl, sl, sr, fr], [sl, rl, rr, sr]
|
|
rows = {}
|
|
for row, (b, t_) in {"front": ("20C6D", "20C6E"), "rear": ("20C64", "20C65")}.items():
|
|
q, qt = pts(b), pts(t_)
|
|
rows[row] = dict(centre_rel_girder5=rel(centre(q)), rotation_deg=round(angle_deg(q[0], q[1]), 3),
|
|
drawn_bottom_along=round(math.dist(q[0], q[1]), 1), drawn_bottom_across=round(math.dist(q[1], q[2]), 1),
|
|
drawn_top_along=round(math.dist(qt[0], qt[1]), 1), drawn_top_across=round(math.dist(qt[1], qt[2]), 1),
|
|
src=f"D1 평면도 {b} (bottom), {t_} (top)")
|
|
checks = []
|
|
g4 = [line("20C26")[0], line("20C26")[1], line("20C27")[1], line("20C28")[1]]
|
|
for a, b in zip(ped, g4):
|
|
checks.append(dict(item="girder4 pedestal vertex", dx=round(b[0] - (a[0] - PITCH), 2), dy=round(b[1] - a[1], 2)))
|
|
for row, hs in (("front", ("20C6D", "20C70")), ("rear", ("20C64", "20C67"))):
|
|
c5, c4 = centre(pts(hs[0])), centre(pts(hs[1]))
|
|
checks.append(dict(item=f"girder4 {row} pad centre", dx=round(c4[0] - (c5[0] - PITCH), 2), dy=round(c4[1] - c5[1], 2)))
|
|
bearings = []
|
|
for no in range(1, 11):
|
|
r = el[no]
|
|
g, row = (no - 1) % 5 + 1, "front" if no <= 5 else "rear"
|
|
pr = rows[row]
|
|
bearings.append(dict(no=no, type=r["교량받침"], girder_line=g, row=row,
|
|
centre=[round(pr["centre_rel_girder5"][0] + (g - 5) * PITCH, 1), pr["centre_rel_girder5"][1]],
|
|
rotation_deg=pr["rotation_deg"], el_x=round(float(r["EL.'X'"]) * 1000), h1=int(r["h1"]),
|
|
h2=int(r["h2"]), A=int(r["A"]), B=int(r["B"]), inferred=g <= 2))
|
|
p["bearing_seats"] = dict(
|
|
_note="pedestal (h1) per girder line split front/rear; pad (h2) = frustum (A+200)x(B+200) -> (A+100)x(B+100)",
|
|
plan_datum=list(PLAN), pitch_x=PITCH, reference_girder=5, pad_margin_bottom=200, pad_margin_top=100,
|
|
pad_margin_src="P1 rule (D1 평면도 pad = (A+200)x(B+200) / (A+100)x(B+100))",
|
|
pedestal_girder5=dict(front_half=[rel(q) for q in front_half], rear_half=[rel(q) for q in rear_half],
|
|
rotation_deg=round(angle_deg(fl, rl), 3),
|
|
src="D1 평면도 20C1E~20C21 (2500x1200), 21A3F (front/rear split)"),
|
|
pad_rows=rows, bearings=bearings, translation_checks=checks,
|
|
src={"h1_h2_A_B_ELX": "D2 교량받침 ELEVATION TABLE (table wins over drawn sizes: P1 user rule)",
|
|
"row_mapping": "front NO.1-5 (S1 851), rear NO.6-10 (S1 950); 851+1299+950=3100",
|
|
"girder_mapping": "NO.k / NO.k+5 -> girder line k, left->right",
|
|
"drawn_vs_table": "D1 평면도 pads 900x840 (B=640) and 측면도 h1 163.4/139, h3 181 = P1 values not updated "
|
|
"-> CONFLICT, table used"})
|
|
return p
|
|
|
|
|
|
def expected_g1(el):
|
|
tops = {1: 42456, 3: 42602, 5: 42748} # D1 'EL. 42.456' / 'EL. 42.602' / 'EL. 42.748'
|
|
step = 72.9 # drawn step rise (no dimension text) [derived]
|
|
step_top = {str(k): round(tops[1] + (k - 1) * step) if k in (2, 4) else tops[k] for k in range(1, 6)}
|
|
step_top["2"], step_top["4"] = 42529, 42675
|
|
sx = [-6000, -6000 + 2219, -6000 + 2219 + 2502, 6000 - 2274 - 2502, 6000 - 2274, 6000]
|
|
return {
|
|
"footing": {"dx": 10350, "dy": 7800, "z0": EL_FT_BOT, "thickness": 2200, "src": "D1 '10.350' '7.800' 'EL. 25.123' '2.200'"},
|
|
"lean": {"margin": 100, "thickness": 100, "src": "D1 lean +100 each side, 100 thick"},
|
|
"column": {"d": 2000, "height": 12133, "src": "D1 단면 C-C '2.000', 정면도 '12.133'"},
|
|
"coping": {"dx": 12400, "dy": 3100, "z_top_left": 42456, "rise_upper": 1500, "rise_lower": 1500,
|
|
"z_top_right": 42748, "z_top_center": 42602, "flat_from_tip": 5100, "end_inset": 200, "radius": 12000,
|
|
"src": "D1 '12.400' '3.100' 'EL. 42.456' '1.500' x2 'EL. 42.748' 'EL. 42.602' '5.100' '200' 'R=12.000'"},
|
|
"coping_outline": [[-1100, Z_COL_TOP], [1100, Z_COL_TOP], [6200, Z_COL_TOP + 1500], [6000, 42748], [sx[4], 42748],
|
|
[sx[4], 42675], [sx[3], 42675], [sx[3], 42602], [sx[2], 42602], [sx[2], 42529], [sx[1], 42529],
|
|
[sx[1], 42456], [-6000, 42456], [-6200, Z_COL_TOP + 1500]],
|
|
"coping_outline_src": "D1 정면도 외형 from dims '200','2.219','3@2.502=7.507','2.274','200' and EL texts; "
|
|
"intermediate step EL 42.529 / 42.675 from drawn risers 72.9 [derived]",
|
|
"dxf_arc_centres": {"datum_x": FX, "datum_y": COL_TOP_Y, "datum_el": Z_COL_TOP,
|
|
"left": [5207.5, 6039.2], "right": [19111.2, 6039.2],
|
|
"src": "DXF ARC 20E8B / 20E89 centre; datum CS-CNTL 21539 / coping bottom line 20E87"},
|
|
"piles": {"nx": 6, "ny": 5, "pitch_x": 1750, "pitch_y": 1550, "edge": 800, "d": 600, "length": 25900,
|
|
"head_embedment": 100, "src": "D1 '5@1.750=8.750' '4@1.550=6.200' '800' 'Φ600' 'L=25.9M'"},
|
|
"seats": {"table": {str(n): [round(float(r["EL.'X'"]) * 1000), int(r["h1"]), int(r["h2"]), int(r["A"]), int(r["B"])]
|
|
for n, r in el.items()},
|
|
"table_columns": ["EL.'X'", "h1", "h2", "A", "B"], "girders": 5, "step_top": step_top,
|
|
"step_x": {str(i + 1): [sx[i], sx[i + 1]] for i in range(5)},
|
|
"row_y": {"front": -(1550 - int(el[1]["S1"])), "rear": 1550 - int(el[6]["S1"])},
|
|
"pitch_chain": [3, 7507], "pedestal_plan": [1250, 1200], "pad_margin": [200, 100],
|
|
"rear_first_from_tip": 1223,
|
|
"src": "D2 ELEVATION TABLE; D1 EL texts + drawn steps; S1 851/950; '3@2.502=7.507'; 대좌 2500x1200; '1.223'"},
|
|
}
|
|
|
|
|
|
def main():
|
|
el, _ = table()
|
|
P = json.loads((PIER / "params.json").read_text(encoding="utf-8"))
|
|
E = json.loads((PIER / "expected.json").read_text(encoding="utf-8"))
|
|
stamp = time.strftime("%Y%m%d_%H%M%S")
|
|
for f in ("params.json", "expected.json"):
|
|
shutil.copy2(PIER / f, PIER / "backup" / f"{Path(f).stem}_before_R1_{stamp}.json")
|
|
P.update(params_r1(el))
|
|
P.pop("_meta", None)
|
|
P["_meta"] = {"structure": "운곡교 P2 교각 (완도방향)", "status": "R1 형상 판독 2026-10-12 (Agent 초안, 사용자 확정 전)",
|
|
"units": "mm; Z = EL x 1000", "frame": "X 교축직각, Y 교축, 원점 = 교각 중심",
|
|
"sources": {"D1": "C0050205-028 교각도(1) P2", "D2": "C0050205-029 교각도(2) P2", "D3": "C0050205-030 교각도(3) P2"}}
|
|
P["pier_id"] = "P2"
|
|
P["display"]["hidden_ids"] = []
|
|
P["display"]["src"] = "R8 팔레트(2026-10-07); 숨김 없음(요구사항 기본값 '모두 표시')"
|
|
E["pier_id"] = "P2"
|
|
E["G1"] = expected_g1(el)
|
|
(PIER / "params.json").write_text(json.dumps(P, ensure_ascii=False, indent=1), encoding="utf-8")
|
|
(PIER / "expected.json").write_text(json.dumps(E, ensure_ascii=False, indent=1), encoding="utf-8")
|
|
s = P["bearing_seats"]
|
|
print(json.dumps({k: P[k] for k in ("footing", "column", "piles")}, ensure_ascii=False))
|
|
print("coping", {k: v for k, v in P["coping"].items() if k != "src"})
|
|
print("pedestal rot", s["pedestal_girder5"]["rotation_deg"], "rows", {r: (v["centre_rel_girder5"], v["rotation_deg"]) for r, v in s["pad_rows"].items()})
|
|
print("translation checks", s["translation_checks"])
|
|
print("bearing 1/6", s["bearings"][0], s["bearings"][5])
|
|
print("expected step_top", E["G1"]["seats"]["step_top"], "row_y", E["G1"]["seats"]["row_y"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|