import { describe, expect, it } from 'vitest'; import { buildPier } from '../src/core/geometry'; import { validateInput } from '../src/core/validate'; import { parseAbutments, toAbutInput } from '../src/core/abut/parse'; import { buildAbut } from '../src/core/abut/geometry'; import { abutSoles, resolveBearingRule } from '../src/core/abut/bearing-table'; import type { Issue, PierInput } from '../src/core/types'; import { loadInputWorkbook, loadP1, p1Pairs } from './helpers'; // 받침별 받침높이 (2026-11-10): h3 = 그 받침의 받침높이 H − T_ref, 거더 하면 = z + H. const CTX = { pairs: p1Pairs() }; const withHeights = (input: PierInput, h: Record): PierInput => ({ ...input, bearings: input.bearings.map((b) => ({ ...b, heightMm: b.no in h ? h[b.no] : b.heightMm })), }); describe('교각 받침별 받침높이', () => { it('reads 받침높이 from the bearing sheet (P1: 211 for every bearing, so h3 stays 181)', () => { const p1 = loadP1(); expect(p1.bearings.map((b) => b.heightMm)).toEqual(Array(10).fill(211)); expect(buildPier(p1, CTX).blocks.every((b) => b.plan.h3 === 181)).toBe(true); }); it('each bearing gets h3 = its H − T_ref; the block (z − pad − drop) does not move', () => { const base = buildPier(loadP1(), CTX); const m = buildPier(withHeights(loadP1(), { 3: 174, 8: 188 }), CTX); const h3 = (no: number) => m.blocks.find((b) => b.no === no)!.plan.h3; expect(h3(3)).toBe(144); expect(h3(8)).toBe(158); expect(h3(1)).toBe(181); for (const b of m.blocks) { const o = base.blocks.find((x) => x.no === b.no)!; expect(b.top).toBeCloseTo(o.top, 9); expect(b.bottom).toBeCloseTo(o.bottom, 9); } }); it('girder underside = z + H: pad top + h3 + T − (T − T_ref) − … equals z + H for every bearing', () => { const input = withHeights(loadP1(), { 2: 176, 7: 183 }); const m = buildPier(input, CTX); for (const b of m.blocks) { const H = input.bearings.find((x) => x.no === b.no)!.heightMm!; const z = input.bearings.find((x) => x.no === b.no)!.z; expect(b.padTop + (b.plan.h3 + b.plan.T) / 1000).toBeCloseTo(z + H / 1000, 9); } }); it('a typed h3 (솔플레이트 입력) wins over the bearing heights', () => { const input = { ...withHeights(loadP1(), { 3: 174 }), sole: { h3Mm: 200 } }; expect(buildPier(input, CTX).blocks.every((b) => b.plan.h3 === 200)).toBe(true); }); it('without 받침높이 the default h3 181 is used', () => { const input = { ...loadP1(), bearings: loadP1().bearings.map(({ heightMm: _h, ...b }) => b) }; expect(buildPier(input, CTX).blocks.every((b) => b.plan.h3 === 181)).toBe(true); }); it('S01 (E) when a 받침높이 is not above T_ref, so no body could be built', () => { const issues: Issue[] = []; expect(validateInput(withHeights(loadP1(), { 4: 30 }), issues)).toBe(false); expect(issues.filter((i) => i.code === 'S01').map((i) => i.message)).toEqual([expect.stringContaining('받침 4번')]); }); }); describe('교대 받침별 받침높이', () => { const a1 = () => parseAbutments(loadInputWorkbook()).abutments.find((a) => a.name === 'A1')!; it('different heights per bearing give different h3 and no AP07 warning', () => { const wb = loadInputWorkbook(); wb.Sheets['받침']['I16'] = { t: 'n', v: 260 }; const { abutments, issues } = parseAbutments(wb); expect(issues.some((i) => i.code === 'AP07')).toBe(false); const rec = abutments.find((a) => a.name === 'A1')!; const soles = abutSoles(buildAbut(toAbutInput(rec)), resolveBearingRule(), rec.bearingHeightMm); expect(soles.map((s) => s.h3)).toEqual([245, 245, 230, 245, 245]); }); it('a typed h3 wins for every bearing', () => { const rec = a1(); const soles = abutSoles(buildAbut(toAbutInput(rec)), resolveBearingRule({ h3Mm: 200 }), rec.bearingHeightMm); expect(soles.every((s) => s.h3 === 200)).toBe(true); }); });