/** * 교대 받침 상세 삽도 (2026-11-03, 시안 ② 교량받침 카드): 평면, 교축 방향 단면, 솔플레이트 상세. 순수 문자열 함수. * 입력은 받침 표에서 이미 계산한 값뿐이다(교대 모델을 다시 읽지 않는다). 치수는 mm, EL은 m. */ export interface AbutBearingDetail { no: number; /** 블럭 평면 (정사각), 몰탈패드 평면 (정사각), mm. */ blockMm: number; padMm: number; /** 솔플레이트 교직 L × 교축 W (mm). 받침 본체 평면은 입력이 없어 그리지 않는다. */ L: number; W: number; /** 높이 (mm): 블럭 콘크리트 h1, 패드 h2, 받침 h3, 솔플레이트 h4 = T. */ h1: number; h2: number; h3: number; h4: number; T1: number; T2: number; /** 블럭 평면 회전 (deg), 교대축 직각 기준. */ rotDeg: number; /** 솔플레이트 경사 (%). */ S: number; seatEl: number; z: number; } const INK = '#18212b'; const MUTED = '#55606e'; const ORANGE = '#c46a1a'; const text = (x: number, y: number, s: string, anchor = 'middle', fill = INK) => `${s}`; const square = (cx: number, cy: number, w: number, h: number, style: string) => ``; /** 평면: 블럭 1200, 몰탈패드 1000 (점선), 솔플레이트 L × W (주황). 블럭은 받침 방위각만큼 돈다. */ export function abutBearingPlanSvg(d: AbutBearingDetail): string { const k = 0.17; // px per mm const cx = 150; const cy = 130; const turn = `transform="rotate(${(-d.rotDeg).toFixed(3)} ${cx} ${cy})"`; return [ ``, ``, square(cx, cy, d.blockMm * k, d.blockMm * k, `fill="#dfe5ec" stroke="${MUTED}"`), square(cx, cy, d.padMm * k, d.padMm * k, `fill="none" stroke="${MUTED}" stroke-dasharray="4 3"`), square(cx, cy, d.L * k, d.W * k, `fill="#f0c9a0" fill-opacity="0.6" stroke="${ORANGE}"`), ``, text(cx, cy - (d.blockMm * k) / 2 - 8, `${d.blockMm}`), text(cx, cy + 4, `솔플 ${d.L} × ${d.W}`), text(cx, cy + (d.blockMm * k) / 2 + 16, `패드 ${d.padMm} (점선)`, 'middle', MUTED), text(cx, 262, `받침 ${d.no} · 회전 ${d.rotDeg.toFixed(2)}° (방위각)`, 'middle', MUTED), '', ].join(''); } /** 교축 방향 단면 (높이 2배 과장): 블럭 h1, 패드 h2, 받침 h3, 솔플레이트 h4. E = h1 + h2 + h3 + h4. */ export function abutBearingSectionSvg(d: AbutBearingDetail): string { const kx = 0.17; const ky = 0.34; // 2× exaggerated heights const cx = 150; const base = 215; const E = d.h1 + d.h2 + d.h3 + d.h4; const layer = (bottom: number, h: number, w: number, style: string) => ``; const at = (mm: number) => base - mm * ky; const right = cx + (d.blockMm * kx) / 2 + 6; return [ ``, ``, layer(0, d.h1, d.blockMm, `fill="#dfe5ec" stroke="${MUTED}"`), layer(d.h1, d.h2, d.padMm, `fill="#c9d1da" stroke="${MUTED}"`), layer(d.h1 + d.h2, d.h3, d.L - 100, `fill="#b9c3ce" stroke="${INK}"`), layer(d.h1 + d.h2 + d.h3, d.h4, d.L, `fill="#f0c9a0" stroke="${ORANGE}"`), text(right, at(d.h1 / 2) + 4, `h1 ${d.h1}`, 'start'), text(right, at(d.h1 + d.h2 / 2) + 4, `h2 ${d.h2}`, 'start'), text(right, at(d.h1 + d.h2 + d.h3 / 2) + 4, `h3 ${d.h3}`, 'start'), text(right, at(E - d.h4 / 2) + 4, `h4 ${d.h4}`, 'start'), text(cx, at(E) - 10, `E = ${E}`), text(24, base + 16, `교좌면 EL.${d.seatEl.toFixed(3)}`, 'start', MUTED), text(24, at(d.h1 + d.h2) - 4, `z EL.${d.z.toFixed(3)}`, 'start', MUTED), text(cx, 244, '교축 방향 · 높이 2배 과장', 'middle', MUTED), '', ].join(''); } /** 솔플레이트 상세: 길이 L, 두께 T1 → T2 (경사 S). 두께는 과장해서 그린다. */ export function abutSolplateSvg(d: AbutBearingDetail): string { const kx = 0.3; const ky = 2.2; // thickness exaggerated const x0 = 45; const x1 = x0 + d.L * kx; const base = 120; return [ ``, ``, text(x0 - 4, base - (d.T1 * ky) / 2 + 4, `T1 ${d.T1}`, 'end'), text(x1 + 4, base - (d.T2 * ky) / 2 + 4, `T2 ${d.T2}`, 'start'), text((x0 + x1) / 2, base + 18, `L ${d.L} · W ${d.W}`), text((x0 + x1) / 2, base - Math.max(d.T1, d.T2) * ky - 10, `S ${d.S.toFixed(3)} % · T ${d.h4}`), text(165, 180, '솔플레이트 상세 · 두께 과장 · 가정 (QA-15)', 'middle', MUTED), '', ].join(''); }