/* ============================================================================= * B05_Profile_UI_Drainage_Spans.ts * 배수유역도(평면)에서 **구간형 구조물이 놓인 자리**를 계획선 위에 띠로 그린다. * * 왜 (계획서 3-6) — 산마루측구·도수로·옹벽처럼 **구간**으로 놓이는 시설은 종단에는 띠로 * 보이는데 평면에는 아무 표시가 없었다. 「노선 위에 임의 구간을 얹는 부품이 없다」가 * 그동안의 걸림돌이었는데, 계획선 표본(`strengthSamples`)이 **1m 간격이라 배열 인덱스가 * 곧 누가거리**여서 구간 → 화면 선은 잘라 붙이기만 하면 된다. * * 그리는 자리 — **계획선 바로 위, 강도 색칠·마커 아래**. 굵고 반투명해서 계획선을 덮지 * 않는다. 이름은 안 적는다(종단에 이미 있고, 지도에 글자를 얹으면 눈금·유역 번호와 겹친다). * ========================================================================== */ import type { RoutePoint } from "../B04_PreProcess/B04_PreProcess_UI_RouteSamples"; import type { StructureInstance, StructureType } from "./B05_Profile_Api_Structures"; /** 계획선 위에 얹을 구간 하나 — 누가거리 두 값과 색. */ export interface RouteSpanBand { startM: number; endM: number; /** 구조물 레지스트리의 표시색(`style.color`). */ color: string; } /** 띠 굵기(px) — 계획선(2px 안팎)보다 확실히 굵되 유역 채움을 가리지 않는 값. */ const BAND_WIDTH_PX = 7; /** * 구간 띠를 그린다. 표본이 없거나 구간이 비면 아무 것도 하지 않는다. * * 인덱스는 **누가거리(m)** 다 — 표본이 1m 간격이라 그렇다(`resampleRoute`). 범위를 벗어난 * 값은 잘라 쓰고, 한 점짜리 구간(시작=끝)은 짧은 토막으로라도 보이게 한 칸을 준다. */ export function drawRouteSpans( context: CanvasRenderingContext2D, samples: ReadonlyArray, spans: ReadonlyArray, toScreen: (x: number, y: number) => [number, number], ): void { if (samples.length < 2 || spans.length === 0) return; const last = samples.length - 1; context.save(); context.lineCap = "round"; context.lineJoin = "round"; context.lineWidth = BAND_WIDTH_PX; for (const span of spans) { const from = Math.max(0, Math.min(last, Math.floor(Math.min(span.startM, span.endM)))); const to = Math.max(from + 1, Math.min(last, Math.ceil(Math.max(span.startM, span.endM)))); context.beginPath(); for (let index = from; index <= to; index += 1) { const [x, y] = toScreen(samples[index].x, samples[index].y); if (index === from) context.moveTo(x, y); else context.lineTo(x, y); } context.strokeStyle = span.color; context.globalAlpha = 0.45; context.stroke(); context.globalAlpha = 1; } context.restore(); } /** * 구조물 정본 + 타입 레지스트리 → 띠 목록. **종단 레인과 같은 자료**를 본다(계획서 3-6). * * · 구간형(`interval`)만 띠가 된다 — 점형 시설은 마커로 이미 보인다. * · 색은 레지스트리 표시색을 그대로 쓴다(여기서 새로 정하지 않는다). * · 시작·끝이 없으면 기준 측점으로 대신한다. 그것도 없으면 뺀다. */ export function routeSpansFromStructures( structures: ReadonlyArray, types: ReadonlyArray, ): RouteSpanBand[] { const colorOf = new Map(types.map((type) => [type.type_id, type.style?.color])); const spans: RouteSpanBand[] = []; for (const item of structures) { if (item.placement !== "interval") continue; const start = item.start_m ?? item.chainage_m ?? null; const end = item.end_m ?? item.chainage_m ?? null; if (start === null || end === null) continue; spans.push({ startM: Math.min(start, end), endM: Math.max(start, end), color: colorOf.get(item.type_id) ?? "#8a8a8a", }); } return spans; }