import type { PavementEntry } from "@util/common_util_structure_pavement"; import type { CrossDesign } from "./B06_Section_Api_Fetch"; import { pavementLayers, rockBoundarySegments, } from "./logic/B06_Section_Cross_Design_Surface_Geom"; const SVG_NS = "http://www.w3.org/2000/svg"; export function appendRockBoundaryOverlay( svg: SVGElement, samples: Array<{ offset_m?: number; elevation_m?: number | null; valid: boolean }>, offsetM: number, x: (offset: number) => number, y: (elevation: number) => number, ): void { for (const segment of rockBoundarySegments(samples, offsetM)) { const polyline = document.createElementNS(SVG_NS, "polyline"); polyline.setAttribute( "points", segment.map((p) => `${x(p.offset_m)},${y(p.elevation_m)}`).join(" "), ); polyline.setAttribute("class", "b06-chart__rock-boundary"); svg.append(polyline); } } /** 포장사양 → 무늬(88) — 콘크리트 빗금 · 아스콘 점 · 쇄석 돌. 그 밖 = 채움만. */ const PAVE_MARKS: Record = { 콘크리트: { size: 6, marks: '' }, 아스콘: { size: 5, marks: '', }, 쇄석: { size: 9, marks: '' + '', }, }; /** 무늬 id 는 문서 안에서 유일 — 카드 · 다시 그리기마다 새 번호(물넘이 빗금과 같은 방식). */ let paveSeq = 0; type PaveMark = { type_id: string; start_m?: number | null; end_m?: number | null }; /** 포장층 범위 표식(종류 · 시작 · 끝)이 그 노면공인가 — 겹친 노면공 가름. */ function isPaveLayerOf(data: DOMStringMap, mark: PaveMark): boolean { const [low, high] = [mark.start_m ?? 0, mark.end_m ?? 0].sort((a, b) => a - b); return ( data.paveType === mark.type_id && Math.abs(Number(data.paveStart) - low) < 0.01 && Math.abs(Number(data.paveEnd) - high) < 0.01 ); } /** * 고른 노면공의 포장층만 `is-active`(152-5) — 범위 표식(종류 · 시작 · 끝)이 맞는 층. 겹친 노면공은 하나만. * `mark` 없음 = 모두 풂. */ export function highlightPavement(root: ParentNode, mark?: PaveMark): void { for (const layer of root.querySelectorAll(".b06-chart__pavement")) { layer.classList.toggle("is-active", !!mark && isPaveLayerOf(layer.dataset, mark)); } } /** 누른 자리의 포장층 → 그 노면공(189-1 · 형상 누름 = 목록 고름) · 범위 표식 없는 층 · 밖 = undefined. */ export function pavementAt( target: EventTarget | null, marks: ReadonlyArray, ): T | undefined { const layer = (target as Element | null)?.closest?.(".b06-chart__pavement"); if (!layer?.dataset.paveType) return undefined; return marks.find((mark) => isPaveLayerOf(layer.dataset, mark)); } /** * 포장층 — 층 꼭짓점 = `pavementLayers`(두께 · 확폭 켬/끔 · 차례로 쌓음) · 여기는 무늬 · 범위 표식 · * 그리기. 없으면 카드 포장 켬일 때 표준 두께 채움만. */ export function appendPavementOverlay( svg: SVGElement, design: CrossDesign, x: (offset: number) => number, y: (elevation: number) => number, pavement?: readonly PavementEntry[] | null, ): void { for (const { entry, corners } of pavementLayers(design, pavement)) { const points = corners.map((p) => `${x(p.offset_m)},${y(p.elevation_m)}`).join(" "); const polygon = document.createElementNS(SVG_NS, "polygon"); polygon.setAttribute("points", points); polygon.setAttribute("class", "b06-chart__pavement"); if (entry) { // 범위 표식 — 고른 노면공의 포장층만 강조(152-5 · 겹친 노면공 가름) polygon.dataset.paveType = entry.type_id; polygon.dataset.paveStart = String(entry.start_m); polygon.dataset.paveEnd = String(entry.end_m); } const spec = entry?.spec; const mark = spec ? PAVE_MARKS[spec] : undefined; if (spec && mark) { // 무늬는 바탕 채움째 한 패턴 — 다각형 한 장(B07 도면 수확이 겹선을 안 담음) const id = `b06-pave-hatch-${(paveSeq += 1)}`; const defs = document.createElementNS(SVG_NS, "defs"); defs.innerHTML = `${mark.marks}`; svg.append(defs); polygon.style.fill = `url(#${id})`; polygon.dataset.paveSpec = spec; } svg.append(polygon); } }