import { describe, expect, it } from 'vitest'; import { buildPier } from '../src/core/geometry'; import { P1_COPING_GEOM, sideRowLevels } from '../src/core/coping-geom'; import { shearUHeight, shearULength } from '../src/core/rebar/coping'; import { rebarSummary } from '../src/core/rebar/summary'; import { buildRebar3D } from '../src/view/rebar3d'; import { loadP1 } from './helpers'; const L = 12000; describe('① 코핑 끝단 높이 h1 · 테이퍼 높이 h2 · Level 폭 (1단계, 승인 2026-10-23)', () => { it('the P1 geometry reproduces the document U heights (890.9, 2178.8, Level 2300)', () => { expect(shearUHeight(300, L)).toBeCloseTo(890.9, 1); expect(shearUHeight(4550, L)).toBeCloseTo(2178.8, 1); expect(shearUHeight(6000, L)).toBeCloseTo(2300, 6); }); it('the model defaults to the P1 coping geometry and the P1 shear lengths', () => { const model = buildPier(loadP1()); expect(model.copingGeom).toEqual(P1_COPING_GEOM); expect(model.endBottom).toBeCloseTo(43.411 - 1.0, 6); expect(model.levelBottom).toBeCloseTo(43.411 - 1.0 - 1.5, 6); }); it('a higher end height h1 lowers the coping ends and adds the same height to every U', () => { const base = buildPier(loadP1()); const high = buildPier({ ...loadP1(), copingH1Mm: 1200 }); expect(high.endBottom).toBeCloseTo(base.endBottom - 0.2, 6); expect(high.levelBottom).toBeCloseTo(base.levelBottom - 0.2, 6); const geomH1 = { ...P1_COPING_GEOM, h1: 1200 }; expect(shearUHeight(300, L, geomH1)).toBeCloseTo(shearUHeight(300, L) + 200, 6); expect(shearULength(300, L, geomH1)).toBeCloseTo(shearULength(300, L) + 400, 6); }); it('a taper h2 changes the Level underside and the height at the Level', () => { const high = buildPier({ ...loadP1(), copingH2Mm: 1800 }); expect(high.levelBottom).toBeCloseTo(43.411 - 1.0 - 1.8, 6); expect(shearUHeight(6000, L, { ...P1_COPING_GEOM, h2: 1800 })).toBeCloseTo(2600, 6); }); it('a wider Level changes the Level zone in the model and the shear-bar Level half-width', () => { const wide = buildPier({ ...loadP1(), levelWidthMm: 2500 }); expect(wide.copingGeom.levelWidth).toBe(2500); expect(wide.levelBottom).toBeCloseTo(buildPier(loadP1()).levelBottom, 6); }); it('side-row levels keep their P1 values from the Level underside (assumption, 명세 확인 필요)', () => { const levels = sideRowLevels(40.911); expect(levels.zBottom).toBeCloseTo(41.288, 6); expect(levels.zBreak).toBeCloseTo(42.538, 6); }); it('the coping rebar follows a changed geometry: the rows and the 3D build without error, the positions are unchanged', () => { const base = rebarSummary(buildPier(loadP1())).rows; const changed = buildPier({ ...loadP1(), copingH1Mm: 1200, copingH2Mm: 1600, levelWidthMm: 2300 }); const rows = rebarSummary(changed).rows; expect(rows.find((r) => r.name === 'C10')!.count).toBe(base.find((r) => r.name === 'C10')!.count); expect(rows.find((r) => r.name === 'C9')!.count).toBe(base.find((r) => r.name === 'C9')!.count); expect(buildRebar3D(changed).byName.get('C10')!.length).toBeGreaterThan(0); }); });