/** * 교대 확대기초 · 말뚝 평면 삽도 (2026-11-04, 시안 ② 확대기초·말뚝 카드). 순수 문자열 함수. * 생성본 값으로 그린다: 저판(skew 평행사변형, 굵은 선), 벽체 윤곽(실선), 교대축(점선), 말뚝 3열(원), 열 위치, 폭 치수. * 가로(s)와 세로(t) 축척이 같다. 위가 전면, 아래가 배면이다. */ export interface AbutFootingDrawing { /** 교대축 길이 L (m). */ lengthM: number; /** s per m of t along the wing (skew): s = s_axis + k·t. */ skewK: number; /** t (m): 저판 앞굽 끝, 뒷굽 끝, 벽체 전면, 벽체 배면. */ footingFrontM: number; footingBackM: number; wallFrontM: number; wallBackM: number; /** 말뚝 열: t (m), s (m, 교대축 기준, skew 전), 열 이름 (1열 · 전면 …). */ rows: { t: number; s: number[]; name: string }[]; pileDiameterM: number; /** 열 위치 치수 (mm, 배면부터): 배면 연단, 간격, 간격, 전면 연단. */ rowDimsMm: [number, number, number, number]; footingWidthMm: number; } const INK = '#18212b'; const MUTED = '#55606e'; const label = (x: number, y: number, s: string, anchor = 'start', fill = INK) => `${s}`; export function abutFootingPlanSvg(d: AbutFootingDrawing): string { const k = 36; // px per m, both directions const sMin = Math.min(d.skewK * d.footingFrontM, d.skewK * d.footingBackM) - 0.4; const sMax = d.lengthM + Math.max(d.skewK * d.footingFrontM, d.skewK * d.footingBackM) + 0.4; const tTop = d.footingFrontM + 0.4; const X = (s: number, t: number) => 20 + (s + d.skewK * t - sMin) * k; const Y = (t: number) => 14 + (tTop - t) * k; const W = 20 + (sMax - sMin) * k + 170; const H = Y(d.footingBackM - 0.4) + 34; const quad = (t0: number, t1: number, style: string) => ``; const parts: string[] = []; parts.push(quad(d.footingBackM, d.footingFrontM, `fill="#e6ebf0" stroke="${INK}" stroke-width="2"`)); parts.push(quad(d.wallBackM, d.wallFrontM, `fill="none" stroke="${MUTED}"`)); parts.push(``); parts.push(label(X(d.lengthM, 0) + 6, Y(0) + 4, '교대축', 'start', '#c0392b')); const r = Math.max(2, (d.pileDiameterM / 2) * k); for (const row of d.rows) { for (const s of row.s) parts.push(``); const tLabel = `${row.t >= 0 ? '+' : '−'}${Math.round(Math.abs(row.t) * 1000)}`; parts.push(label(X(d.lengthM, row.t) + r + 8, Y(row.t) + 4, `${row.name} · t ${tLabel} · ${row.s.length}본`)); } const [e1, g1, g2, e2] = d.rowDimsMm; parts.push(label(20, H - 10, `${d.footingWidthMm} = 뒷굽쪽 ${e1} · ${g1} · ${g2} · ${e2} 전면 · 위 = 전면`, 'start', MUTED)); return [ ``, ...parts, '', ].join(''); }