/**
* 확대기초·말뚝 그림 (2단계, 승인 2026-10-23): plan and side views as SVG strings, drawn from the model (footings, piles,
* columns) and the input values. Pure: no DOM, so that it can be tested. Units: m in the model; the labels show mm.
* Both views use one scale (FOOTING_PX_PER_M), so they can sit side by side. The side view breaks the column and the piles
* (끊음 표시) so that its height stays near the plan's.
*/
import type { PierModel } from '../core/geometry';
export const FOOTING_PX_PER_M = 22;
/** Side view: the column is shown this far above the footing top, the piles this far below the footing bottom (m). */
const COLUMN_STUB = 3;
const PILE_STUB = 4;
const mm = (v: number) => Math.round(v * 1000);
/** A break mark (끊음): two short slanted strokes "//" centred on (x, y), so the member is shown as cut. */
function breakMark(x: number, y: number, halfWidth: number): string {
const d = 0.12;
return ``;
}
function svgOf(x0: number, y0: number, w: number, h: number, label: string, body: string): string {
const pw = Math.round(w * FOOTING_PX_PER_M);
const ph = Math.round(h * FOOTING_PX_PER_M);
return ``;
}
/** Plan view: the footing rectangle (length along the 교축, width across), the pile circles, the column circle, the axis. */
export function footingPlanSvg(model: PierModel): string {
const f = model.footings[0];
if (!f) return '';
const pad = 0.8;
const x0 = f.s - f.length / 2 - pad;
const y0 = -f.width / 2 - pad;
const w = f.length + 2 * pad;
const h = f.width + 2 * pad;
const piles = model.piles.filter((p) => Math.abs(p.s - f.s) <= f.length / 2 + 1e-6);
const parts: string[] = [];
parts.push(``);
for (const p of piles) {
parts.push(``);
}
for (const c of model.columns) {
parts.push(``);
}
parts.push(``);
parts.push(`${mm(f.length)}`);
parts.push(`${mm(f.width)}`);
return svgOf(x0, y0, w, h, '확대기초·말뚝 평면', parts.join(''));
}
/** Side view (elevation, EL up): the footing, the piles, and the column stub above the footing, both broken (끊음). */
export function footingSideSvg(model: PierModel): string {
const f = model.footings[0];
if (!f) return '';
const piles = model.piles.filter((p) => Math.abs(p.s - f.s) <= f.length / 2 + 1e-6);
const pileTop = piles.length ? Math.max(...piles.map((p) => p.top)) : f.bottom;
const pileLen = piles.length ? piles[0].top - piles[0].bottom : 0;
const pad = 0.8;
const x0 = f.s - f.length / 2 - pad;
const w = f.length + 2 * pad;
// Visible band in EL: from the column stub above the footing top, down to the pile stub below the footing bottom.
const topEl = f.top + COLUMN_STUB;
const botEl = f.bottom - PILE_STUB;
const y0 = -topEl;
const h = topEl - botEl;
const parts: string[] = [];
parts.push(``);
for (const p of piles) {
const from = Math.max(p.bottom, botEl);
const to = Math.min(p.top, f.bottom);
if (to > from) parts.push(``);
}
const col = model.columns[0];
if (col) {
parts.push(``);
// Break mark on the column stub, at its middle.
parts.push(breakMark(col.s, -f.top - COLUMN_STUB / 2, col.diameter / 2 + 0.15));
}
if (piles.length) {
// Break mark across the pile group, in the lower stub (the pile label sits below it).
parts.push(breakMark(f.s, -(botEl + 0.6), f.length / 2));
}
parts.push(`h1 ${mm(f.top - f.bottom)}`);
if (piles.length) {
parts.push(`말뚝 ${mm(pileLen)} (끊음)`);
}
void pileTop;
return svgOf(x0, y0, w, h, '확대기초·말뚝 측면', parts.join(''));
}