"""R3 (footing rebar) reading of the Ungok P2 drawings -> params.footing_rebar / rebar_marks(B*) / expected.G3. Drawn facts (D1 front / side view dots, relative to the view centre, z above the footing bottom): - layer heights as P1 (F1 rule): 122 / 178 / 227.5 / 277 / 2073 / 2127; B10 rings 350..1950 @200, dots x 5093 / y 3818 - front (Y-direction bars at X): ends +-5075, series -4875 + 125 i (79) -> B2/B4; -4875 + 250 i (40) -> B1, B3 - side (X-direction bars at Y): as P1 (+-3800, -3625 + 125 i (59) / + 250 i (30)) - mid layers: B12 (front) 550/1350: +-1875, 2875, 3875; 950/1750: +-375 .. +-4375 -> 32 drawn (D3 38); B11 (side) as P1 -> 28 (D3 28) - B9 EREC lines x -4625 .. 4625 @250 (38), y -3500 .. 3500 @250 (29), z 150..2100 -> checker 551 drawn (D3 493) Conflicts B9 / B12 count: drawn adopted (P1 rule F2), schedule difference recorded as planned FAIL. """ import json 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)) from extract_r2 import schedule # noqa: E402 def series(start, pitch, n): return [float(start + pitch * i) for i in range(n)] def sym(vals): return sorted([-v for v in vals] + list(vals)) def main(): S = schedule() 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_R3_{stamp}.json") fr = P["footing_rebar"] y_bot = sorted([-5075.0, 5075.0] + series(-4875, 125, 79)) y_250 = sorted([-5075.0, 5075.0] + series(-4875, 250, 40)) L = fr["layers"] L["bottom_Y1"]["pos"], L["bottom_Y3"]["pos"], L["top_Y"]["pos"] = y_bot, y_250, y_250 mid_y = {"550.0": sym([1875.0, 2875.0, 3875.0]), "950.0": sym([375.0, 1375.0, 2375.0, 3375.0, 4375.0]), "1350.0": sym([1875.0, 2875.0, 3875.0]), "1750.0": sym([375.0, 1375.0, 2375.0, 3375.0, 4375.0])} L["mid_Y"]["levels"] = mid_y fr["a_x"], fr["a_y"] = float(S["B5"]["A"]) * 1000, float(S["B1"]["A"]) * 1000 fr["a_src"] = "D3 B5 a '10.150' (X bars), B1 a '7.600' (Y bars)" fr["b9"].update({"grid_x": series(-4625, 250, 38), "count": 551, "src": "D1 정면 EREC x -4625..4625 @250 (chain '450','37@250=9.250'), 측면 -3500..3500 @250; " "checker parity as P1 -> 551 drawn; D3 개수 493 (= P1 value) -> CONFLICT, drawn adopted"}) fr["b10"].update({"size_x": float(S["B10"]["A"]) * 1000, "size_y": float(S["B10"]["B"]) * 1000, "src": "D1 정면 '150','9@200=1.800'; dots x=5093 / y=3818; D3 B10 a 10.198 b 7.644"}) fr["_note"] = "Footing rebar B1-B12 (Ungok P2); z above the footing bottom EL 25.123" rows = {} for m in ("B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "B10", "B11", "B12"): r = S[m] rows[m] = [m, r["형식"], r.get("직경", ""), r["개수"], r["A"]] + [r[k] for k in ("B", "C") if k in r] + [r["길이"], r["총길이"]] P["rebar_marks"][m] = {"dia": r["_dia"], "shape": r["형식"]} fr["schedule"] = rows fr["counts_check"] = {"bottom_Y1": len(y_bot), "bottom_X2": len(L["bottom_X2"]["pos"]), "bottom_Y3": len(y_250), "bottom_X4": len(L["bottom_X4"]["pos"]), "top_Y": len(y_250), "top_X": len(L["top_X"]["pos"]), "mid_X": sum(len(v) for v in L["mid_X"]["levels"].values()), "mid_Y": sum(len(v) for v in mid_y.values())} sched = {m: [round(float(S[m]["길이"]) * 1000), int(S[m]["개수"])] for m in rows} g3 = E["G3"] g3.update({ "schedule": sched, "dia": {m: int(S[m]["_dia"][1:]) for m in rows}, "rows": {"Y_BOT": {"ends": [-5075, 5075], "start": -4875, "pitch": 125, "n": 79}, "Y_250": {"ends": [-5075, 5075], "start": -4875, "pitch": 250, "n": 40}, "X_BOT": {"ends": [-3800, 3800], "start": -3625, "pitch": 125, "n": 59}, "X_250": {"ends": [-3800, 3800], "start": -3625, "pitch": 250, "n": 30}}, "b10_size": [10198, 7644], "b9_grid": {"x": {"start": -4625, "pitch": 250, "n": 38}, "y": {"start": -3500, "pitch": 250, "n": 29}}, "src": "D3 L·수량 (P2); D1 '100','200','78@125=9.750' / '39@250=9.750' / '100','175','58@125=7.250' / '29@250'; " "F1 층 높이(작도점, P1과 같음); '150','9@200=1.800'; '450','37@250=9.250' / '300','28@250=7.000'; " "EREC 150~2100; 중간층 작도점 [derived]", }) pf = [x for x in E.get("planned_fail", []) if x["gate"] != "G3"] pf += [{"gate": "G3", "item": "B12 bar count vs schedule", "reason": "작도 32본 채택 vs D3 38본 (P1 F2 규칙, P1도 28/34)", "approved": "P1 결정(2026-10-07) 준용 — P2 사용자 확인 대기"}, {"gate": "G3", "item": "B9 bar count vs schedule", "reason": "작도 551본(38×29 엇갈림) 채택 vs D3 493본(P1 값 그대로)", "approved": "P1 결정(2026-10-07) 준용 — P2 사용자 확인 대기"}] E["planned_fail"] = pf (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") print("counts", fr["counts_check"], "b9", fr["b9"]["count"], "a", fr["a_x"], fr["a_y"], "b10", fr["b10"]["size_x"]) print("schedule", sched) if __name__ == "__main__": main()