/** * 교대 상세 (2026-11-03, 교각 상세와 같은 개념): the values the 삽도 shows, computed from the model. Read only. * Each row is [label, value]. Lengths in mm, levels in m, as in the 삽도. */ import type { AbutRecord } from './parse'; import { buildAbut, type AbutModel } from './geometry'; import { toAbutInput } from './parse'; import { buildMembers, type AbutMembers } from './members'; import { buildWings } from './wings'; const m3 = (v: number) => `${v.toFixed(3)} m`; const mm = (v: number) => `${Math.round(v)} mm`; export function abutSummaryRows(rec: AbutRecord): [string, string][] { const model: AbutModel = buildAbut(toAbutInput(rec)); const members = buildMembers(model, { bottomEl: rec.foundationBottomEl, pileLengthMm: rec.pileLengthMm }, rec.shape); const wings = buildWings(model, members, rec.wings, rec.wingDims); const shape = members.shape; const first = model.bearings[0]; const L = model.length; const rows: [string, string][] = [ ['형식 / 기초 형식', `${rec.type} · ${rec.foundation}`], ['교좌면 EL (구간 ①)', m3(rec.seatEl)], ['기초 하단 EL', m3(rec.foundationBottomEl)], ['지반고 EL', m3(rec.groundEl)], ['저판 상면 EL', m3(members.footingTopEl)], ['교대축 길이 (IP1 → IP3)', m3(L)], ['skew', `${rec.skewDeg.toFixed(2)}°`], ['종단경사 i', `${(rec.slopeI * 100).toFixed(2)} %`], ['받침블럭 높이 (패드 포함)', mm(first.blockHeightMm)], ['벽체 높이 (구간 ①)', m3(members.wallSegments.find((w) => Math.abs(w.seatEl - first.seatEl) < 1e-9)?.heightM ?? NaN)], ['벽체 두께 · 전면', `${Math.round(shape.wallFrontM * 1000 - shape.wallBackM * 1000)} mm · ${Math.round(shape.wallFrontM * 1000)} mm`], ['흉벽 높이 · 두께', `${Math.round(shape.parapetHeightM * 1000)} mm · ${Math.round(-shape.parapetBackM * 1000)} mm`], ['흉벽 상단 EL (IP1 끝 / IP3 끝)', `${members.parapetTopStartEl.toFixed(3)} / ${members.parapetTopEndEl.toFixed(3)} m`], ['브라켓 상면 EL (구간 ①)', m3(first.seatEl + shape.parapetHeightM - shape.bracketHeightM)], ['흉벽 경사', `${(members.parapetSlope * 100).toFixed(3)} %`], ['계단 경계 (IP3 쪽에서)', model.stepBounds.map((s) => (L - s).toFixed(3)).reverse().join(' / ') + ' m'], ['저판 폭 (앞굽 끝 / 뒷굽 끝)', `${Math.round(shape.footingFrontM * 1000)} / ${Math.round(shape.footingBackM * 1000)} mm`], ['말뚝 (열 × 본수)', `${members.piles.length} × ${members.piles[0]?.s.length ?? 0} = ${members.piles.length * (members.piles[0]?.s.length ?? 0)} 본`], ['말뚝 선단 EL', members.pileBottomEl === undefined ? '—' : m3(members.pileBottomEl)], ['수축줄눈 (ceil(L/10) − 1)', `${Math.max(0, Math.ceil(L / 10) - 1)} 개`], ]; if (wings.length > 0) { const w = wings[0]; rows.push(['날개벽 경사', `${model.wing ? (model.wing.slopePct).toFixed(2) : '—'} %`]); rows.push(['날개벽 뒷굽 거리 (교대축 기준)', mm(w.heelDistanceM * 1000)]); } if (model.wing) rows.push(['접속슬래브 폭', mm(model.wing.slabWidthMm)]); return rows; } /** 입력값 rows of the 교대 panel (시안 "A1 입력값" card): what the user typed, read only. */ export function abutInputRows(rec: AbutRecord): [string, string][] { const rows: [string, string][] = [ ['형식 / 기초 형식', `${rec.type} (${rec.type === 'A1' ? '시점' : '종점'}) · ${rec.foundation}`], ['교좌면 EL (구간 ①)', `${rec.seatEl.toFixed(3)} m`], ['기초 하단 EL', `${rec.foundationBottomEl.toFixed(3)} m`], ['지반고 EL', `${rec.groundEl.toFixed(3)} m`], ['skew', `${rec.skewDeg.toFixed(2)}°`], ['종단경사 i', `${(rec.slopeI * 100).toFixed(2)} %`], ['말뚝 길이', `${Math.round(rec.pileLengthMm)} mm`], ['IP', 'IP1 → IP3 직선'], ['받침', `${rec.bearings.length}개 · z = 패드 상면`], ]; return rows; } /** The 8 detail rows shown in the collapsed 상세 (the rest stay in the 삽도 view). */ export const ABUT_DETAIL_LABELS = [ '벽체 높이 (구간 ①)', '흉벽 상단 EL (IP1 끝 / IP3 끝)', '브라켓 상면 EL (구간 ①)', '저판 상면 EL', '계단 경계 (IP3 쪽에서)', '말뚝 (열 × 본수)', '말뚝 선단 EL', '접속슬래브 폭', ]; /** 구간 표 of the 벽체·흉벽 card (시안 하단): one row per 교좌면 step (2026-11-03). Values come from the model and members. */ export const ABUT_SEGMENT_HEADER = ['구간', '시작 (m)', '끝 (m)', '교좌면 EL (m)', '받침블럭 높이 (mm)', '벽체 높이 (m)']; export function abutSegmentRows(model: AbutModel, members: AbutMembers): string[][] { const circled = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧']; const bearings = [...model.bearings].sort((a, b) => a.s - b.s); return members.wallSegments.map((seg, k) => [ circled[k] ?? String(k + 1), seg.sFrom.toFixed(3), seg.sTo.toFixed(3), seg.seatEl.toFixed(3), String(bearings[k]?.blockHeightMm ?? '—'), (seg.seatEl - members.footingTopEl).toFixed(3), ]); }