import type { PavementEntry } from "@util/common_util_structure_pavement"; import type { CrossDesign } from "./B06_Section_Api_Fetch"; 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 { const segments: string[][] = []; let current: string[] = []; for (const sample of samples) { const elevation = sample.elevation_m; if (sample.valid === false || elevation === null || !Number.isFinite(elevation ?? NaN)) { if (current.length > 1) segments.push(current); current = []; continue; } current.push(`${x(sample.offset_m ?? 0)},${y((elevation as number) + offsetM)}`); } if (current.length > 1) segments.push(current); for (const points of segments) { const polyline = document.createElementNS(SVG_NS, "polyline"); polyline.setAttribute("points", points.join(" ")); polyline.setAttribute("class", "b06-chart__rock-boundary"); svg.append(polyline); } } type Edge = { offset_m: number; elevation_m: number }; /** 포장사양 → 무늬(88) — 콘크리트 빗금 · 아스콘 점 · 쇄석 돌. 그 밖 = 채움만. */ const PAVE_MARKS: Record = { 콘크리트: { size: 6, marks: '' }, 아스콘: { size: 5, marks: '', }, 쇄석: { size: 9, marks: '' + '', }, }; /** 무늬 id 는 문서 안에서 유일 — 카드 · 다시 그리기마다 새 번호(물넘이 빗금과 같은 방식). */ let paveSeq = 0; /** 확폭 끈 구간 — 규격 폭 끝(좌 = left − 확폭좌 · 우 = right + 확폭우 · 표고는 두 끝 사이 직선). */ function standardEdges(edges: { left: Edge; right: Edge }, design: CrossDesign) { const { left, right } = edges; const span = right.offset_m - left.offset_m || 1; const at = (offset: number): Edge => ({ offset_m: offset, elevation_m: left.elevation_m + ((right.elevation_m - left.elevation_m) * (offset - left.offset_m)) / span, }); return { left: at(left.offset_m - (design.widening_left_m ?? 0)), right: at(right.offset_m + (design.widening_right_m ?? 0)), }; } /** * 포장층 — 노면선(= 포장 윗면) 아래로 두께만큼. 노면공 범위(`section.pavement`)면 그 두께 · 무늬 · * 확폭 켬/끔(여러 건은 아래로 차례로 쌓음) · 없으면 카드 포장 켬일 때 표준 두께 채움만. */ export function appendPavementOverlay( svg: SVGElement, design: CrossDesign, x: (offset: number) => number, y: (elevation: number) => number, pavement?: readonly PavementEntry[] | null, ): void { const edges = design.carriageway_edges ?? design.road_edges; if (!edges) return; const layers = pavement?.length ? pavement : design.paved ? [null] : []; let depth = 0; for (const entry of layers) { const thickness = entry?.thickness_m ?? design.pavement_thickness_m ?? 0.2; const { left, right } = entry && !entry.widening_on ? standardEdges(edges, design) : edges; const points = [ `${x(left.offset_m)},${y(left.elevation_m - depth)}`, `${x(right.offset_m)},${y(right.elevation_m - depth)}`, `${x(right.offset_m)},${y(right.elevation_m - depth - thickness)}`, `${x(left.offset_m)},${y(left.elevation_m - depth - thickness)}`, ].join(" "); depth += thickness; const polygon = document.createElementNS(SVG_NS, "polygon"); polygon.setAttribute("points", points); polygon.setAttribute("class", "b06-chart__pavement"); 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); } }