Files
Aislo/B05_Profile/B05_Profile_UI_Drainage_Spans.ts
T
eomsangdonandClaude Opus 5.5 281c71b55b refactor(logic): 348-1 순수 계산 TS 49 파일 <페이지>/logic/ 로 옮김 — 이름 앞머리 그대로 · 동작 고침 없음
- 옮김: B04 2 · B05 23 · B06 11 · A00_Common/design 13 (9,299 줄) · 이미 logic/ 에 있던 16 파일 그대로
- 안 옮김: 묶음 입구(package.json build:*) · 파이썬 · 순수지만 계산 아닌 8 (보기 결선 · 콜백 · API 타입 · 갈아끼움 알림판)
- import 266 줄(142 파일) · 시험 · 주석 · 짝 표시 문자열 경로 고침 · B05 · B06 CLAUDE.md 짝 줄 logic/
- 시험 도우미 새 경로: 가짜 require 열쇠 · B05()/B06() 부름 · 형제 import 자리 · tsc 묶어 돌리는 시험 · 정규식 · rglob
- design_view_cross 줄 한도 시험 685 → 688 (긴 import 경로 줄바꿈 3줄)
- 확인: typecheck 0 · 서버 묶음 넷(corridor · server-calc · b07-extent · b07-note) 다시 지어 코드 같음(//#region 경로 주석만 다름) · 관련 시험 270 파일 — 남은 실패 19 는 옮기기 전과 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
2026-10-16 13:44:50 +09:00

89 lines
3.9 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_Drainage_Spans.ts
* 배수유역도(평면)에서 **구간형 구조물이 놓인 자리**를 계획선 위에 띠로 그린다.
*
* 왜 (계획서 3-6) — 산마루측구·도수로·옹벽처럼 **구간**으로 놓이는 시설은 종단에는 띠로
* 보이는데 평면에는 아무 표시가 없었다. 「노선 위에 임의 구간을 얹는 부품이 없다」가
* 그동안의 걸림돌이었는데, 계획선 표본(`strengthSamples`)이 **1m 간격이라 배열 인덱스가
* 곧 누가거리**여서 구간 → 화면 선은 잘라 붙이기만 하면 된다.
*
* 그리는 자리 — **계획선 바로 위, 강도 색칠·마커 아래**. 굵고 반투명해서 계획선을 덮지
* 않는다. 이름은 안 적는다(종단에 이미 있고, 지도에 글자를 얹으면 눈금·유역 번호와 겹친다).
* ========================================================================== */
import type { RoutePoint } from "../B04_PreProcess/logic/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<RoutePoint>,
spans: ReadonlyArray<RouteSpanBand>,
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<StructureInstance>,
types: ReadonlyArray<StructureType>,
): 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;
}