- 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>
135 lines
9.9 KiB
Python
135 lines
9.9 KiB
Python
"""Create the app pier '새재1교P1' (tapered T on a spread footing, shared pedestals, rectangular plan; scope G1 + G5) through
|
|
the web client: new pier (Ulsan template as a starting copy), upload a copy of RAG/새재1교P1.dwg, then write params / expected
|
|
from the reading (판독/g1_values.json; DOC/02_분석/분석_새재1교P1교각도면판독_2026-10-15_v0.1.00.md) and decisions N1-N3.
|
|
usage: python make_pier.py (refuses if the pier already exists)
|
|
"""
|
|
import json
|
|
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"))
|
|
import app as webapp # noqa: E402
|
|
import store # noqa: E402
|
|
|
|
NAME = "새재1교P1"
|
|
V = json.loads((HERE / "판독" / "g1_values.json").read_text(encoding="utf-8"))
|
|
cf = V["coping_front"]
|
|
# the five top steps (drawing lines 2F1F..2F27: y 38348.5 + 101 x k; z = y - 5816.5); bounds = bearing-line step edges
|
|
# step z = EL.'X' - h1 - h2 of the ELEVATION TABLE: the front row gives 32531 / 32632 / 32733 / 32834 / 32936, the rear row
|
|
# 32532 / 32633 / 32734 / 32835 / 32936 (rows differ by 1 mm = table rounding; drawing lines 32532.0 / 32633.1 / ...).
|
|
# The model uses the mean (x.5) so that both rows stay within 0.5 mm of the table.
|
|
STEPS = [{"x0": -6000.0, "x1": -3722.5, "z": 32531.5}, {"x0": -3722.5, "x1": -1215.6, "z": 32632.5},
|
|
{"x0": -1215.6, "x1": 1291.3, "z": 32733.5}, {"x0": 1291.3, "x1": 3798.2, "z": 32834.5},
|
|
{"x0": 3798.2, "x1": 6000.0, "z": 32936.0}]
|
|
FLAT, TIP, ZB, ZTIP = 1200.0, 6000.0, 29532.0, 31232.0
|
|
prof = [(-FLAT, ZB), (FLAT, ZB), (TIP, ZTIP)]
|
|
for s in reversed(STEPS):
|
|
prof += [(s["x1"], s["z"]), (s["x0"], s["z"])]
|
|
prof += [(-TIP, ZTIP)]
|
|
TABLE = {1: (32790, 209), 2: (32891, 209), 3: (32992, 209), 4: (33093, 209), 5: (33194, 208),
|
|
6: (32776, 194), 7: (32877, 194), 8: (32978, 194), 9: (33079, 194), 10: (33180, 194)}
|
|
SOLE = {1: (-0.460, 23, 24, 22), 2: (-0.458, 23, 24, 22), 3: (-0.456, 23, 24, 22), 4: (-0.454, 23, 24, 22), 5: (-0.452, 23, 24, 22),
|
|
6: (-0.504, 24, 26, 22), 7: (-0.502, 24, 26, 22), 8: (-0.500, 24, 26, 22), 9: (-0.498, 24, 26, 22), 10: (-0.496, 24, 26, 22)}
|
|
pads = {int(k): v for k, v in V["pads_outer"].items()}
|
|
step_z = lambda no: STEPS[(no - 1) % 5]["z"] # NO.1-5 front row / 6-10 rear row lie on step 1..5 (left -> right)
|
|
YF = pads[1]["centre"][1] # front (lower) pad row y
|
|
YR = pads[6]["centre"][1] # rear (upper) pad row y
|
|
pedestals = []
|
|
for i, pd in enumerate(V["pedestals"]):
|
|
x0, x1 = pd["x"]
|
|
ya, yb = pd["y"]
|
|
zs = step_z(i + 1)
|
|
pedestals.append({"no": i + 1, "corners": [[x1, ya], [x0, ya], [x0, yb], [x1, yb]], "z_bottom": zs,
|
|
"top": {"y0": YF, "z0": zs + TABLE[i + 1][1], "y1": YR, "z1": zs + TABLE[i + 6][1]},
|
|
"src": pd["src"] + "; 상면 앞 + h1 → 뒤 + h1 평면 경사 (N2 [추론])"})
|
|
bearings = [{"no": no, "type": "받침", "row": "front" if no <= 5 else "rear", "centre": pads[no]["centre"],
|
|
"rotation_deg": pads[no]["rotation_deg"], "seat_z": step_z(no), "el_x": TABLE[no][0], "h1": TABLE[no][1],
|
|
"h2": 50, "A": 550, "B": 550, "pedestal": (no - 1) % 5} for no in range(1, 11)]
|
|
|
|
params = {
|
|
"_schema": "pier-T/1", "pier_id": "P1",
|
|
"_meta": {"structure": "새재1교 P1 교각 (T형, 직접기초, 직선 경사 코핑, 평면 직사각, 받침 10개·대좌 5개 공유)",
|
|
"status": "형상(G1)·받침(G5) 판독 2026-10-15, 결정 N1~N3 사용자 확정. 철근 G2~G4 미판독(이번 범위 밖)",
|
|
"units": "mm; Z = EL x 1000", "frame": "X 코핑 길이 방향, Y 평면 위쪽(선형 진행 방향), 원점 = 기둥 중심",
|
|
"sources": {"D1": "RAG/새재1교P1.dwg (교각 일반도(1)(2) + 구조도(1)~(4))"},
|
|
"reading": "DOC/02_분석/분석_새재1교P1교각도면판독_2026-10-15_v0.1.00.md, Code/새재1교P1판독/판독/g1_values.json"},
|
|
"scope": ["G1", "G5"], "builders": {"G1": "concrete_tapered.py"},
|
|
"lean_concrete": {"size_x": 7900.0, "size_y": 7900.0, "z_bottom": 9932.0, "z_top": 10032.0, "src": V["lean"]["src"]},
|
|
"footing": {"size_x": 7700.0, "size_y": 7700.0, "z_bottom": 10032.0, "thickness": 2600.0, "src": V["footing"]["src"]},
|
|
"column": {"shape": "circular", "diameter": 2200.0, "height": 16900.0, "src": V["column"]["src"]},
|
|
"coping": {"form": "tapered", "profile_xz": [list(p) for p in prof], "width_y": 3000.0, "plan_length": 12000.0,
|
|
"top_length": 12000.0, "z_tip": ZTIP, "z_top_left": STEPS[0]["z"], "z_top_right": STEPS[-1]["z"],
|
|
"plan_corner_radius": 0,
|
|
"side_taper": {"half_width_top": 1500.0, "z_taper_top": ZTIP, "half_width_bottom": 1100.0},
|
|
"skew_deg": 0.0,
|
|
"src": {"profile": cf["src"] + " (Y = 0 단면, 상면 5단)", "side": V["coping_side"]["src"],
|
|
"plan": V["coping_plan"]["src"]}},
|
|
"materials": {"fck": {"lean": "16MPa", "footing": "27MPa", "column": "35MPa", "coping": "35MPa", "seat": "35MPa"},
|
|
"src": "일반도 주기 '버림 CON'C fck = 16 MPa', '구체 fck = 27 MPa'(기초), '구체 fck = 35 MPa'(코핑); 기둥·대좌 35 [추론] (N3)"},
|
|
"bearing_seats": {"_note": "shared pedestals (one per bearing station, both rows), sloped top (N2); pads rotated per bearing",
|
|
"pad_margin_bottom": 200, "pad_margin_top": 100, "pedestals": pedestals, "bearings": bearings,
|
|
"src": "일반도(1) 평면 대좌 28D6~28F9, 패드 2989~29A6; ELEVATION TABLE h1·h2·A·B"},
|
|
"sole_plates": {"table": [{"no": no, "S": SOLE[no][0], "L": 650, "W": 650, "T": SOLE[no][1], "T1": SOLE[no][2], "T2": SOLE[no][3]}
|
|
for no in range(1, 11)],
|
|
"h3": 132, "bearing_size": "AB", "bearing_shrink": 100,
|
|
"colours": {"bearing": [0, 0, 0], "sole_plate": [150, 150, 150]},
|
|
"sole_plate_src": "솔플레이트 상세 TABLE; S < 0 -> 두꺼운 쪽 -Y (운곡교 규칙과 같음 [추론], OPEN #16)",
|
|
"src": "교량받침 A x B (550 x 550), h3 132 (ELEVATION TABLE); T = h4 23 / 24"},
|
|
"rebar_marks": {}, "rebar_marks_src": "철근 G2~G4 다음 Task",
|
|
}
|
|
expected = {
|
|
"_schema": "pier-T-expected/1", "pier_id": "P1", "tol_mm": 1.0,
|
|
"_note": "독립 기대값: 일반도 치수 문자·EL 문자·받침 표. [작도] = 치수 문자 없음(대좌 외곽, 받침 평면 위치·회전)",
|
|
"checkers": {"G1": "g1_pedestal.py"},
|
|
"negative": {"G1": [{"id": "BP02", "d": [3, 0, 0]}, {"id": "BS03", "d": [0, 0, 2]}],
|
|
"G5": [{"id": "BB04", "d": [5, 0, 0]}, {"id": "SP03", "d": [0, 0, 5]}]},
|
|
"planned_fail": [],
|
|
"G1": {"footing": {"dx": 7700, "dy": 7700, "z0": 10032, "thickness": 2600, "src": "'7.700', '2.600', 'EL. 10.032'"},
|
|
"lean": {"dx": 7900, "dy": 7900, "thickness": 100, "src": "버림 '100', 7,900"},
|
|
"column": {"d": 2200, "height": 16900, "src": "'2.200', '16.900'"},
|
|
"coping": {"bottom_flat_half": 1200, "tip_half": 6000, "soffit_rise": 1700, "width_y": 3000, "side_bottom_width": 2200,
|
|
"skew_deg": 0.0, "step_bounds": [-3723, -1216, 1291, 3798],
|
|
"step_z": [32532, 32633, 32734, 32835, 32936],
|
|
"src": "'12.000', '1.300'(좌 끝), '1.704'(우 끝), '1.700', 기둥 폭 '2.200'(평평한 하면 ±1,200: 작도), '3.000'(폭), "
|
|
"상면 단 경계 '2.277, 3@2.507, 2.202'(받침 열 기준); 단 높이 = EL.'X' - h1 - h2 (NO.1~5); 'EL. 32.532', 'EL. 32.936'"},
|
|
"seats": {"n_pedestals": 5,
|
|
"table": {str(no): [TABLE[no][0], TABLE[no][1], 50, 550, 550] for no in range(1, 11)},
|
|
"pos": {str(no): pads[no]["centre"] for no in range(1, 11)},
|
|
"src": "ELEVATION TABLE; 받침 평면 위치 [작도] (평면 치수 '1.024 / 948', S1·S2 '1000 / 997 / 1003' 일치)"}},
|
|
"G5": {"el_x": {str(no): TABLE[no][0] for no in range(1, 11)}, "S": {str(no): SOLE[no][0] for no in range(1, 11)},
|
|
"W": {str(no): 650 for no in range(1, 11)}, "L": 650,
|
|
"T": {str(no): SOLE[no][1] for no in range(1, 11)}, "h3": 132,
|
|
"bearing_size": {str(no): [550, 550] for no in range(1, 11)}, "bearing_shrink": 100,
|
|
"colours": {"bearing": [0, 0, 0], "sole_plate": [150, 150, 150]},
|
|
"src": "ELEVATION TABLE (EL.'X', h3, A, B); 솔플레이트 상세 TABLE (S, L, W, T, T1·T2; S < 0 -> 두꺼운 쪽 -Y)"},
|
|
}
|
|
|
|
if __name__ == "__main__":
|
|
if "--update" in sys.argv: # rewrite params / expected of the existing pier (values fixed after a gate run)
|
|
p = store.Pier(NAME)
|
|
params["display"] = p.load("params")["display"]
|
|
expected["rebar_palette"] = p.load("expected")["rebar_palette"]
|
|
p.save("params", params)
|
|
p.save("expected", expected)
|
|
sys.exit("updated " + str(p.dir))
|
|
if (store.DATA / NAME).exists():
|
|
sys.exit(f"pier exists: {NAME}")
|
|
c = webapp.app.test_client()
|
|
assert c.post("/pier/new", data={"name": NAME, "type": "T형", "template": "tpl:산외교울산P1형"}).status_code == 302
|
|
dwg = ROOT / "RAG" / "새재1교P1.dwg"
|
|
r = c.post(f"/pier/{NAME}/upload", data={"dwg": [(BytesIO(dwg.read_bytes()), dwg.name)]}, content_type="multipart/form-data")
|
|
assert r.status_code == 302, r.status_code
|
|
p = store.Pier(NAME)
|
|
params["display"] = p.load("params")["display"]
|
|
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"] = "신규 판독 (새재1교 P1 판독서 v0.1.00)"
|
|
store.wjson(p.dir / "pier.json", meta)
|
|
print("created", p.dir, "texts", len(p.texts()), "profile", prof)
|