Files
Pier_v1/Code/RampA판독/make_pier.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

137 lines
9.0 KiB
Python

"""Create the app pier 'Ramp-A교P1' (form "single shaft", scope G1 + G5) through the web client:
new pier (Ulsan template as a starting copy), upload copies of the four RAG DWGs, then write params / expected
from the reading (판독/g1_values.json, D1 tables; DOC/02_분석/분석_Ramp-A교P1교각도면판독_2026-10-13_v0.1.00.md).
params = drawn values (handles), user decisions R1-R4 (2026-10-13).
expected = dimension / EL texts and the D1 tables, typed in by hand; two drawn-only inputs marked [작도].
usage: python make_pier.py (refuses if the pier already exists)
"""
import json
import math
import sys
from io import BytesIO
from pathlib import Path
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
APP = ROOT / "Code" / "교각모델링앱"
sys.path.insert(0, str(APP / "web"))
sys.path.insert(0, str(APP))
import app as webapp # noqa: E402
import store # noqa: E402
NAME = "Ramp-A교P1"
V = json.loads((HERE / "판독" / "g1_values.json").read_text(encoding="utf-8"))
cp = V["coping"]
def circle_centre(a, b, r):
"""Centre (lower side) of the radius-r circle through a and b."""
(ax, az), (bx, bz) = a, b
mx, mz = (ax + bx) / 2, (az + bz) / 2
d = math.hypot(bx - ax, bz - az)
h = math.sqrt(r * r - d * d / 4)
ux, uz = (bz - az) / d, -(bx - ax) / d
return min(((mx + h * ux, mz + h * uz), (mx - h * ux, mz - h * uz)), key=lambda c: c[1])
ZB, ZT = V["column"]["z1"], cp["end_left_front"]["bottom"][1]
cx, cz = circle_centre((-1000.0, ZB), (-3800.0, ZT), 5000.0)
bear = {1: dict(el_x=60112, seat_z=cp["top_left"]["z"]), 2: dict(el_x=60427, seat_z=cp["top_right"]["z"])}
params = {
"_schema": "pier-T/1",
"pier_id": "P1",
"_meta": {"structure": "Ramp-A교 P1 교각 (T형 계열: 단주 + Y형 코핑 + 단일 현장타설말뚝)",
"status": "형상(G1)·받침(G5) 판독 2026-10-13, 결정 R1~R4 사용자 확정. 철근 G2~G4 미판독(범위 밖)",
"units": "mm; Z = EL x 1000",
"frame": "X 교축직각(정면도 좌→우), Y 교축(평면도 아래→위), 원점 = 교각 중심",
"sources": {"D1": "RAG/Ramp-A교 P1 교각(1).dwg 일반도", "D2": "(2) 코핑·기둥 배근(1)",
"D3": "(3) 주철근 조립도·재료표", "D4": "(4) 기둥 재료표·떠오름 방지 PLATE"},
"reading": "DOC/02_분석/분석_Ramp-A교P1교각도면판독_2026-10-13_v0.1.00.md, Code/RampA판독/판독/g1_values.json"},
"scope": ["G1", "G5"],
"builders": {"G1": "concrete_shaft.py"},
"pile": {"segments": [{"d": V["pile_lower"]["d"], "z0": V["pile_lower"]["z0"], "z1": V["pile_lower"]["z1"]},
{"d": V["pile_upper"]["d"], "z0": V["pile_upper"]["z0"], "z1": V["pile_upper"]["z1"]}],
"kind": "현장타설말뚝 (암반 근입 Ø2,350 + 상부 Ø2,500)",
"src": f"D1 정면 {V['pile_lower']['src']}; {V['pile_upper']['src']}; 상부 Ø2,500은 '75, 2,350, 75' 표기(케이싱 포함 가능) — 작도선대로 콘크리트"},
"column": {"shape": "circular", "diameter": V["column"]["d"], "z0": V["column"]["z0"], "z1": V["column"]["z1"],
"src": f"D1 정면 {V['column']['src']}"},
"coping": {"form": "y_shaft", "z_bottom": ZB, "z_tip": ZT, "half_bottom": 1000.0,
"soffit": {"centre_x": round(cx, 4), "centre_z": round(cz, 4), "radius": 5000.0},
"z_top_left": cp["top_left"]["z"], "z_top_right": cp["top_right"]["z"], "top_half": 3500.0,
"plan_end": {"corner_x": abs(cp["plan_end_left"]["corner_bottom_x"]), "tip_x": 3800.0,
"half_width": cp["plan_end_left"]["half_width"]},
"side": {"half_width_top": cp["side"]["half_width_top"], "half_width_bottom": 1000.0},
"src": {"front": "D1 정면 856/854/855 상면, 857/7D1 끝면, ARC 501/4FF R5,000 (중심은 기둥면·끝 하단 두 점과 R로 재계산)",
"side": cp["side"]["src"], "plan_end": f"{cp['plan_end_left']['src']} / {cp['plan_end_right']['src']}",
"decisions": "R2 직사각 단면(정면 ∩ 측면), R3 우측 상단 3,500(평면값; 정면 작도 3,495.1)",
"not_modelled": "R1 장식 홈(상세 B, 30x30 2줄)"}},
"materials": {"fck": {"pile": "35MPa", "column": "40MPa", "coping": "40MPa", "seat": "40MPa"},
"src": "D1 NOTE '기둥 (40MPa), 코핑 (40MPa), 현장타설말뚝 (35MPa)'; 대좌 40 [추론]"},
"bearing_seats": {
"_note": "pedestal = C3 (along) x C6 (across) box per bearing (h1), rotated as drawn; pad (h2) = frustum (A+200)x(B+200) -> (A+100)x(B+100)",
"pedestal": {"size_x": 1300.0, "size_y": 1400.0}, "pad_margin_bottom": 200, "pad_margin_top": 100,
"bearings": [dict(no=no, type="7000 kN", centre=V["plan"][str(no)]["pedestal"]["centre"],
rotation_deg=V["plan"][str(no)]["pedestal"]["rotation_deg"], h1=110, h2=50, A=900, B=800,
**bear[no]) for no in (1, 2)],
"src": "D1 평면 49D~4A0 / 4A5~4A8 (대좌), 486~48B (패드·받침), ELEVATION TABLE h1·h2·A·B·C3·C6"},
"sole_plates": {"table": [{"no": no, "S": -5.526, "L": 1000, "W": 900, "T": 50, "T1": 78, "T2": 22} for no in (1, 2)],
"h3": 190, "bearing_size": "AB", "bearing_shrink": 100,
"colours": {"bearing": [0, 0, 0], "sole_plate": [150, 150, 150]},
"sole_plate_src": "D1 솔플레이트 상세 TABLE (S, L, W, T, T1, T2); L 교축 방향; S < 0 -> 두꺼운 쪽 -Y (R4, 운곡교 규칙 [추론])",
"src": "교량받침 = A x B (900 x 800), h3 190 (ELEVATION TABLE); 솔플레이트 T = h4 50"},
"rebar_marks": {},
"rebar_marks_src": "철근 G2~G4 다음 Task",
}
expected = {
"_schema": "pier-T-expected/1",
"_note": "독립 기대값: D1 치수 문자·EL 문자·표에서 옮긴 값. params와 별도. [작도] = 치수 문자 없음(작도값)",
"pier_id": "P1",
"tol_mm": 1.0,
"checkers": {"G1": "g1_shaft.py"},
"negative": {"G1": [{"id": "BS02", "d": [3, 0, 0]}, {"id": "BP01", "d": [0, 0, 2]}],
"G5": [{"id": "BB02", "d": [5, 0, 0]}, {"id": "SP01", "d": [0, 0, 5]}]},
"planned_fail": [],
"G1": {
"pile": {"z_bottom": 32752, "segments": [{"d": 2350, "length": 7500}, {"d": 2500, "length": 5200}],
"src": "'EL=32.752', '75, 2,350, 75', '2,500', '7,500', '5,200'"},
"column": {"d": 2000, "z0": 45452, "length": 12000, "src": "'2,000', 'EL=45.452', '11,500' + '500'"},
"coping": {"top_length": 7000, "bottom_length": 7600, "column_face_half": 1000, "soffit_rise": 1500,
"soffit_radius": 5000, "width_y": 2200, "side_bottom_width": 2000, "z_top_left": 59952,
"z_top_right": 60267, "top_step": 315, "plan_end_corner_x": 3650,
"src": "'7,000', '7,600' (2,800+2,000+2,800), '1,500', '1,000', 'R=5,000', '2,200' (1,100+1,100), "
"'EL=59.952', 'EL=60.267', '315'; plan_end_corner_x [작도] D1 평면 51A"},
"total_height": 27200,
"seats": {"table": {"1": [60112, 110, 50, 900, 800], "2": [60427, 110, 50, 900, 800]},
"pos": {"1": [-2250, 0], "2": [2250, 0]}, "pedestal_along": 1400, "pedestal_across": 1300,
"rotation_deg": {"1": -5.55, "2": -9.194}, "pad_margin": [200, 100],
"src": "ELEVATION TABLE; 평면 '2,250' x2; C3 1,400, C6 1,300; rotation [작도] D1 평면"},
},
"G5": {"el_x": {"1": 60112, "2": 60427}, "S": {"1": -5.526, "2": -5.526}, "W": {"1": 900, "2": 900},
"L": 1000, "T": 50, "h3": 190, "bearing_size": {"1": [900, 800], "2": [900, 800]}, "bearing_shrink": 100,
"thick_end": "-Y", "colours": {"bearing": [0, 0, 0], "sole_plate": [150, 150, 150]},
"src": "ELEVATION TABLE (EL.'X', h3, A, B, h4 = T); 솔플레이트 상세 TABLE (S, L, W, T1 78 / T2 22 -> 두꺼운 쪽 -Y, R4)"},
}
if __name__ == "__main__":
if (store.DATA / NAME).exists():
sys.exit(f"pier exists: {NAME}")
c = webapp.app.test_client()
r = c.post("/pier/new", data={"name": NAME, "type": "T형", "template": "tpl:산외교울산P1형"})
assert r.status_code == 302, r.status_code
dwgs = sorted((ROOT / "RAG").glob("Ramp-A교 P1 교각(*).dwg"))
r = c.post(f"/pier/{NAME}/upload", data={"dwg": [(BytesIO(p.read_bytes()), p.name) for p in dwgs]},
content_type="multipart/form-data")
assert r.status_code == 302, r.status_code
p = store.Pier(NAME)
params["display"] = p.load("params")["display"] # Ulsan display (layers incl. 말뚝, palette)
expected["rebar_palette"] = p.load("expected")["rebar_palette"]
p.save("params", params)
p.save("expected", expected)
meta = store.rjson(p.dir / "pier.json")
meta["template"] = "신규 판독 (Ramp-A교 P1 판독서 v0.1.00)"
store.wjson(p.dir / "pier.json", meta)
print("created", p.dir, "texts", len(p.texts()))