Files
Pier_v1/tests/coping-geom.test.ts
T
ypshimandClaude Haiku 5.5 df6aeb5c46 fix(rebar): side rows on the 250 grid up to the 400 mm top margin, not a fixed count of seven
C12 and C13 come from one grid from the Level underside + 127 at 250 mm, up to top − 400 mm (상면 여유). The fixed count of
seven rows is kept only for callers that give no top. P1 is unchanged (C12 5, C13 2); a taper of 3000 mm now gives C12 11
and C13 2, and the C13 first row is always the last C12 row plus 250.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-23 12:31:58 +09:00

116 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest';
import { buildPier } from '../src/core/geometry';
import { P1_COPING_GEOM, SIDE_COVER_M, sideRowLevels } from '../src/core/coping-geom';
import { sideRows } from '../src/core/rebar/coping';
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, 42.411);
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);
});
});
describe('C12 in the taper band (h2), C13 in the end band (h1)', () => {
const bands = (input: Parameters<typeof buildPier>[0]) => {
const m = buildPier(input);
const lv = sideRowLevels(m.levelBottom, m.endBottom);
const rows = sideRows(lv.zBottom, lv.zBreak);
return { m, rows };
};
it.each([
['P1', {}],
['h1 800', { copingH1Mm: 800 }],
['h1 1200', { copingH1Mm: 1200 }],
['h2 1800', { copingH2Mm: 1800 }],
])('%s: every C12 row is in the taper band and every C13 row in the end band', (_name, change) => {
const { m, rows } = bands({ ...loadP1(), ...change });
for (const z of rows.c12) {
expect(z).toBeGreaterThanOrEqual(m.levelBottom - 1e-9);
expect(z).toBeLessThan(m.endBottom + SIDE_COVER_M);
}
for (const z of rows.c13) {
expect(z).toBeGreaterThanOrEqual(m.endBottom + SIDE_COVER_M - 1e-9);
expect(z).toBeLessThanOrEqual(m.copingRefTop);
}
});
});
describe('side-row grid (250, 상면 여유 400, 격자 연속)', () => {
const rowsFor = (input: Parameters<typeof buildPier>[0]) => {
const m = buildPier(input);
const lv = sideRowLevels(m.levelBottom, m.endBottom, m.copingRefTop);
return { m, rows: sideRows(lv.zBottom, lv.zBreak, lv.zTop) };
};
it('P1: 5 C12 rows and 2 C13 rows, the top row at or below top − 0.4', () => {
const { m, rows } = rowsFor(loadP1());
expect(rows.c12).toEqual([41.288, 41.538, 41.788, 42.038, 42.288]);
expect(rows.c13).toEqual([42.538, 42.788]);
expect(Math.max(...rows.c13)).toBeLessThanOrEqual(m.copingRefTop - 0.4 + 1e-9);
});
it('taper h2 = 3000: C12 from 39.788 to 42.288 (11 rows), C13 2 rows, no row lost to a fixed count', () => {
const { rows } = rowsFor({ ...loadP1(), copingH2Mm: 3000 });
expect(rows.c12.length).toBe(11);
expect(rows.c12[0]).toBeCloseTo(39.788, 3);
expect(rows.c13).toEqual([42.538, 42.788]);
});
it('the C13 first row is the last C12 row plus the pitch (the grid is continuous)', () => {
for (const h2 of [1000, 1500, 1750, 2000, 3000]) {
const { rows } = rowsFor({ ...loadP1(), copingH2Mm: h2 });
if (rows.c12.length && rows.c13.length) {
expect(rows.c13[0] - rows.c12[rows.c12.length - 1]).toBeCloseTo(0.25, 6);
}
}
});
});