From a2340c4d6d74b1668a0d9dd754d720c5c99f181d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 29 Sep 2026 21:55:29 +0900 Subject: [PATCH] =?UTF-8?q?fix(B05):=203D=20=EC=BD=94=EB=A6=AC=EB=8F=84=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=EB=AC=BC=20=E2=80=94=20ringsAt=20=EB=A5=BC?= =?UTF-8?q?=20pushSwept=20=EC=95=9E=EC=97=90=20=EC=84=A0=EC=96=B8=ED=95=B4?= =?UTF-8?q?=20ReferenceError(TDZ)=EB=A5=BC=20=EC=97=86=EC=95=B0=20(PLAN=20?= =?UTF-8?q?24-6a)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 원인: e371c275(2026-08-28)부터 기슭막이 단 반복이 ringsAt 선언 전에 pushSwept 를 불러 기슭막이 있는 프로젝트의 3D 코리도가 서버 사전 생성 · 브라우저 폴백 모두 안 만들어짐 - 시험: 선언 차례(ringsAt · pushSwept · 기슭막이 단 반복) Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_019BVZd1M6WUf1YEy9gbAE1G --- .../B05_Profile_UI_Corridor_Structures.ts | 30 ++++++++-------- .../test_b05_corridor_structures_order.py | 34 +++++++++++++++++++ 2 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 resources/tester/test_b05_corridor_structures_order.py diff --git a/B05_Profile/B05_Profile_UI_Corridor_Structures.ts b/B05_Profile/B05_Profile_UI_Corridor_Structures.ts index 935745ce..9ddf07da 100644 --- a/B05_Profile/B05_Profile_UI_Corridor_Structures.ts +++ b/B05_Profile/B05_Profile_UI_Corridor_Structures.ts @@ -302,6 +302,22 @@ export function buildCorridorStructures( const count = Math.max(2, Math.ceil((to - from) / SWEEP_STEP_M) + 1); return Array.from({ length: count }, (_unused, i) => from + ((to - from) * i) / (count - 1)); }; + /** 링 누가거리 목록으로 프레임을 만든다(보어처럼 촘촘한 간격이 필요할 때). */ + const ringsAt = (chainages: number[]): StructureFrame[] => + chainages.map((at) => { + const frame = frameAt(at); + if (!frame) return stationFrame; + return { + cx: frame.cx, + cy: frame.cy, + leftX: frame.leftX, + leftY: frame.leftY, + dz: baseZ != null && frame.designZ != null ? frame.designZ - baseZ : 0, + }; + }); + + // ⚠ `ringsAt` 는 `pushSwept` 보다 **먼저** 선언한다 — 아래 기슭막이 단 반복이 `pushSwept` 를 곧바로 + // 부르므로 뒤에 두면 ReferenceError(TDZ)로 그 구간 3D 코리도가 통째로 안 만들어진다. const pushSwept = ( kind: "revet" | "basin", points: Array<{ offset: number; elevation: number }>, @@ -348,20 +364,6 @@ export function buildCorridorStructures( } } - /** 링 누가거리 목록으로 프레임을 만든다(보어처럼 촘촘한 간격이 필요할 때). */ - const ringsAt = (chainages: number[]): StructureFrame[] => - chainages.map((at) => { - const frame = frameAt(at); - if (!frame) return stationFrame; - return { - cx: frame.cx, - cy: frame.cy, - leftX: frame.leftX, - leftY: frame.leftY, - dz: baseZ != null && frame.designZ != null ? frame.designZ - baseZ : 0, - }; - }); - /** * 관이 뚫고 지나는 구조물 — 단면을 위·아래 조각으로 갈라 링마다 관 지름만큼 * 벌린다(2026-08-25 사용자: 배관 외측 서피스 기준 관통 컷). 관이 닿지 않는 diff --git a/resources/tester/test_b05_corridor_structures_order.py b/resources/tester/test_b05_corridor_structures_order.py new file mode 100644 index 00000000..d0f3a131 --- /dev/null +++ b/resources/tester/test_b05_corridor_structures_order.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +"""3D 코리도 구조물 — 선언 차례(PLAN 24-6a). + +`ringsAt` 를 `const` 로 `pushSwept` 뒤에 선언했더니, 그 사이 기슭막이 단 반복이 `pushSwept` 를 곧바로 불러 +ReferenceError(TDZ)가 나 기슭막이 있는 구간의 3D 코리도가 통째로 안 만들어졌다(2026-08-28 e371c275 ~). +쓰는 `const` 는 그것을 부르는 첫 호출보다 앞에 선언돼야 한다. +""" + +from pathlib import Path + +SOURCE = ( + Path(__file__).resolve().parents[2] / "B05_Profile" / "B05_Profile_UI_Corridor_Structures.ts" +) + + +def test_ringsAt_는_pushSwept_와_첫_호출보다_앞에_선언(): + source = SOURCE.read_text(encoding="utf-8") + rings = source.index("const ringsAt = ") + swept = source.index("const pushSwept = (") + first_call = ( + source.index("pushSwept(\n") if "pushSwept(\n" in source else source.index("pushSwept(") + ) + assert rings < swept, "pushSwept 가 ringsAt 를 쓴다 — 뒤에 선언하면 TDZ" + # 선언(`const pushSwept = (`) 뒤에 나오는 첫 호출 자리 + first_call = source.index("pushSwept(", swept + len("const pushSwept = (")) + assert rings < first_call + + +def test_스윕_쓰는_const_는_호출보다_앞(): + """pushSwept · pushPierced 가 쓰는 도우미 const 가 첫 호출(기슭막이 단 반복)보다 앞에 있다.""" + source = SOURCE.read_text(encoding="utf-8") + first_revet_loop = source.index("for (const tier of revetLayout.tiers)") + for name in ("const ringChainagesOf = ", "const ringsAt = ", "const pushSwept = ("): + assert source.index(name) < first_revet_loop, name