"""평면(지도)에 **구간형 구조물 띠**가 서는지 — 계획서 3-6 마지막 항목. 왜 (2026-09-07 사용자 지시) — 산마루측구·도수로·옹벽처럼 **구간**으로 놓이는 시설이 종단에는 띠로 보이는데 **평면에는 아무 표시가 없었다**. 계획서에는 「노선 위에 임의 구간을 얹는 부품이 없어 새로 짜야 함」으로 남아 있었다. 새로 짠 것은 작다 — 계획선 표본(`strengthSamples`)이 **1m 간격이라 배열 인덱스가 곧 누가거리**여서, 구간 → 화면 선은 그 토막을 잘라 굵게 긋기만 하면 된다. 이 시험이 지키는 것 — ① 띠를 그리는 부품이 있고 ② 배수유역도가 그것을 계획선 **위**, 강도 색칠 **아래**에 그리며 ③ 띠 자료는 **종단과 같은 자료**(구조물 정본 + 타입 레지스트리)에서 나오고 ④ 구간이 없는 점형 시설은 빠지는 것. """ from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[2] _B05 = PROJECT_ROOT / "B05_Profile" _SPANS = _B05 / "B05_Profile_UI_Drainage_Spans.ts" _RENDER = _B05 / "B05_Profile_UI_Drainage_Render.ts" _PANEL = _B05 / "B05_Profile_UI_Drainage_Panel.ts" _PROFILE = _B05 / "B05_Profile_UI_Profile_Panel.ts" def test_띠를_그리는_부품이_있다(): source = _SPANS.read_text(encoding="utf-8") assert "export function drawRouteSpans" in source # 인덱스 = 누가거리(m) 라는 전제가 코드에 남아 있어야 한다 — 표본 간격이 바뀌면 깨진다. assert "Math.floor(Math.min(span.startM, span.endM))" in source def test_범위_밖_구간도_안전하게_잘린다(): """저장분에 노선보다 긴 구간이 남아 있어도 그리다 죽지 않아야 한다.""" source = _SPANS.read_text(encoding="utf-8") assert "Math.max(0, Math.min(last," in source assert "Math.max(from + 1," in source, "시작=끝인 구간이 사라지지 않게 한 칸을 준다" def test_계획선_위_강도색칠_아래에_그린다(): """쌓는 순서가 뒤집히면 띠가 계획선을 덮거나 마커에 가린다.""" render = _RENDER.read_text(encoding="utf-8") assert "drawRouteSpans(context, scene.strengthSamples" in render # 호출 자리끼리 견준다 — 파일 머리의 import 는 순서 판정에 쓰면 안 된다. order_route = render.index("drawPreparedLayer(context, scene.routeLayer") order_span = render.index("drawRouteSpans(context") order_strength = render.index("drawStrengthLine(context") assert order_route < order_span < order_strength def test_종단과_같은_자료에서_뽑는다(): """색은 레지스트리 표시색, 구간은 구조물 정본 — 새 정본을 만들지 않는다.""" spans = _SPANS.read_text(encoding="utf-8") assert "export function routeSpansFromStructures" in spans assert 'item.placement !== "interval"' in spans, "구간형만 띠로 그린다" assert "type.style?.color" in spans, "색을 새로 정하지 않고 레지스트리 값을 쓴다" profile = _PROFILE.read_text(encoding="utf-8") assert "drainagePanel.setIntervalSpans(routeSpansFromStructures(" in profile def test_목록이_바뀌면_다시_그린다(): """구조물을 넣거나 빼면 띠도 따라와야 한다 — 안 그러면 옛 띠가 남는다.""" profile = _PROFILE.read_text(encoding="utf-8") body = profile[profile.index("setStructures(next: StructureInstance[])") :][:400] assert "syncDrainageSpans()" in body types_body = profile[profile.index("setStructureTypes(types: StructureType[])") :][:300] assert "syncDrainageSpans()" in types_body panel = _PANEL.read_text(encoding="utf-8") assert "setIntervalSpans(spans) {" in panel and "scheduleDraw();" in panel