/* ============================================================================= * B06_Section_UI_Standard_Diagram.ts * 표준 횡단면 설정 패널의 "변수 위치 안내" 모식도. * * 설정 패널의 각 편집값(노폭·노견·측구·절토/성토 경사·횡단경사)이 횡단면의 어느 * 위치를 의미하는지 라벨로 표시하는 스키매틱 SVG다. 실제 비율이 아니라 위치 안내용이며, * config 값과 무관한 고정 도형이다(값은 패널 입력에서 편집). * ========================================================================== */ import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; const SVG_NS = "http://www.w3.org/2000/svg"; function L(key: keyof typeof ui_locales): string { return ui_locales[key][currentLanguageIndex]; } function node(tag: string, attrs: Record, cls?: string): SVGElement { const el = document.createElementNS(SVG_NS, tag); for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, String(v)); if (cls) el.setAttribute("class", cls); return el; } function text(x: number, y: number, value: string, cls: string, anchor = "middle"): SVGElement { const t = node("text", { x, y, "text-anchor": anchor }, cls); t.textContent = value; return t; } /** * 편절편성(좌 절토·우 성토) 표준 단면 모식도를 만든다. 좌표는 위치 안내 전용 고정값. * 좌표 규약(+offset=좌)에 맞춰 절토측(측구 방향)을 왼쪽에 둔다. */ export function buildStandardDiagram(): HTMLElement { const wrap = document.createElement("details"); wrap.className = "b06-std__diagram"; wrap.open = true; const summary = document.createElement("summary"); summary.className = "b06-std__diagram-summary"; summary.textContent = L("B06_Std_Diagram_Title"); summary.title = L("B06_Std_Diagram_Toggle"); wrap.append(summary); // 세로로 긴 형태(300×260) — 사면·측구·라벨 간 여백을 넉넉히 둔다. const svg = node("svg", { viewBox: "0 0 300 260", role: "img", "aria-label": L("B06_Std_Diagram_Title"), }) as SVGSVGElement; svg.setAttribute("class", "b06-std__diagram-svg"); // 지반선(원지반) — 좌측 높고 우측 낮은 사면. 절토/성토의 배경. svg.append( node("polyline", { points: "10,60 95,122 205,138 290,210", fill: "none" }, "b06-diag__ground"), ); // 노면(노견 포함): 위치 안내 모식도라 횡단경사는 반영하지 않고 수평으로 그린다(D-7). const roadLeft = 95; const roadRight = 205; const shoulderL = 108; // 노견 좌 경계 const shoulderR = 192; // 노견 우 경계 const roadY = 128; svg.append( node( "polyline", { points: `${roadLeft},${roadY} ${roadRight},${roadY}`, fill: "none" }, "b06-diag__road", ), ); // 노견 경계 눈금(좌/우) svg.append( node("line", { x1: shoulderL, y1: roadY - 5, x2: shoulderL, y2: roadY + 5 }, "b06-diag__tick"), node("line", { x1: shoulderR, y1: roadY - 5, x2: shoulderR, y2: roadY + 5 }, "b06-diag__tick"), ); // 중심선(계획고): 노면 중앙 수직 파선. 라벨 행(y=104)은 비워 노폭 라벨과 겹치지 않게 끊는다. const centerX = (roadLeft + roadRight) / 2; svg.append( node("line", { x1: centerX, y1: 38, x2: centerX, y2: 96 }, "b06-diag__center"), node("line", { x1: centerX, y1: 112, x2: centerX, y2: 162 }, "b06-diag__center"), ); // 측구(절토측=좌): 노면 좌끝에서 아래로 파는 사다리꼴. // 깊이는 기존 16 대비 약 60%(10)로 낮추고, 좌·우 벽 기울기를 4.5로 동일하게 맞춰 // 상단(80~95)과 하단(84.5~90.5)의 중심이 모두 87.5가 되도록 좌우 대칭을 유지한다. const ditchDepth = 10; const ditchWallInset = 4.5; svg.append( node( "polygon", { points: `${roadLeft},${roadY} ${roadLeft - ditchWallInset},${roadY + ditchDepth} ${roadLeft - 15 + ditchWallInset},${roadY + ditchDepth} ${roadLeft - 15},${roadY}`, }, "b06-diag__ditch", ), ); // 절토 사면(좌): 측구 바깥에서 원지반까지 상향. svg.append(node("line", { x1: roadLeft - 15, y1: roadY, x2: 26, y2: 66 }, "b06-diag__cut")); // 성토 사면(우): 노면 우끝에서 원지반까지 하향. svg.append(node("line", { x1: roadRight, y1: roadY, x2: 278, y2: 210 }, "b06-diag__fill")); // 노견 좌 · 노폭 · 노견 우를 한 행(y=104)에 나란히 둔다. 노견 라벨은 바깥쪽 끝이 아니라 // 중앙 노폭 라벨 옆까지 안쪽으로 당겨 세 라벨이 중심(150) 기준 대칭이 되게 한다. const labelLineY = 104; svg.append( text(centerX - 22, labelLineY, L("B06_Std_Diagram_ShoulderL"), "b06-diag__label-sm", "end"), text(centerX, labelLineY, L("B06_Std_Diagram_Road"), "b06-diag__label"), text(centerX + 22, labelLineY, L("B06_Std_Diagram_ShoulderR"), "b06-diag__label-sm", "start"), ); // 나머지 라벨. svg.append( text(centerX, 30, L("B06_Std_Diagram_Center"), "b06-diag__label"), text(roadLeft - 23, roadY + 24, L("B06_Std_Diagram_Ditch"), "b06-diag__label-sm", "middle"), text(46, 54, L("B06_Std_Diagram_Cut"), "b06-diag__label-sm", "middle"), text(252, 196, L("B06_Std_Diagram_Fill"), "b06-diag__label-sm", "middle"), ); wrap.append(svg); const caption = document.createElement("p"); caption.className = "b06-std__diagram-caption"; caption.textContent = L("B06_Std_Diagram_Caption"); wrap.append(caption); return wrap; }