Files
Pier_v1/tests/bearing-layout.test.ts
T
ypshimandClaude Haiku 5.5 2f67efd449 fix(bearing): C4 and C5 are the 교각축 distances from the bearing to the block edges (승인 2026-10-26)
- bearingEdgeDistances measures along the 교각축: 분리 600 / 600; 일체 from the
  pair block centred on the mean 교각축 position (P1 outer margins 557).
- Table headings updated; C1 · C2 · C3 on the plan are measured on the 교축 from the drawn positions.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-26 13:17:50 +09:00

93 lines
4.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { buildPier, type BlockModel } from '../src/core/geometry';
import { bearingEdgeDistances, integratedCheck, PAIR_ECCENTRICITY_LIMIT_MM, pairEccentricities } from '../src/core/bearing-layout';
import { validatePier } from '../src/core/validate';
import type { Issue } from '../src/core/types';
import { loadP1 } from './helpers';
const integrated = buildPier({ ...loadP1(), bearingLayout: '일체' });
const model = buildPier(loadP1());
function block(no: number, row: '전열' | '후열', s: number): BlockModel {
return { no, row, s, n: 0, k: 0, g: 0, bottom: 0, top: 0, padTop: 0, rowSkewDeg: 0 } as BlockModel;
}
describe('받침 배치 규칙 (승인 2026-10-26)', () => {
it('C4 and C5 from the block edges along the 교축: 분리 blocks are centred, so both are 600', () => {
const d = bearingEdgeDistances(model);
expect(d).toHaveLength(10);
for (const x of d) {
expect(x.leftMm).toBeCloseTo(600, 0);
expect(x.rightMm).toBeCloseTo(600, 0);
}
});
it('일체: C4 and C5 along the 교각축 from each bearing to the block edges, from the pair block (centred on the mean)', () => {
const ib = integrated.integratedBlocks[0];
const d = bearingEdgeDistances(integrated);
const f = d.find((x) => x.no === 1 && x.row === '전열')!;
const k = d.find((x) => x.no === 6 && x.row === '후열')!;
const fb = integrated.blocks.find((b) => b.no === 1)!;
const kb = integrated.blocks.find((b) => b.no === 6)!;
expect(f.leftMm).toBeCloseTo((fb.s - (ib.s - 0.6)) * 1000, 6);
expect(f.rightMm).toBeCloseTo(((ib.s + 0.6) - fb.s) * 1000, 6);
expect(k.rightMm).toBeCloseTo(((ib.s + 0.6) - kb.s) * 1000, 6);
// the outer margins (front C4, back C5) are equal on P1
expect(f.leftMm).toBeCloseTo(k.rightMm, 6);
});
it('the front and back bearings of one number are about 85 mm apart on P1, so 일체 is allowed', () => {
const pairs = pairEccentricities(model.blocks);
expect(pairs).toHaveLength(5);
for (const p of pairs) {
expect(p.eccentricityMm).toBeGreaterThan(80);
expect(p.eccentricityMm).toBeLessThan(90);
expect(p.allowed).toBe(true);
}
});
it('an eccentricity above 100 mm forbids 일체 for that pair (분리 우선)', () => {
expect(PAIR_ECCENTRICITY_LIMIT_MM).toBe(100);
const pairs = pairEccentricities([block(1, '전열', 1.0), block(1, '후열', 1.12), block(2, '전열', 3), block(2, '후열', 3.09)]);
expect(pairs.find((p) => p.frontNo === 1)!.allowed).toBe(false);
expect(pairs.find((p) => p.frontNo === 2)!.allowed).toBe(true);
});
it('pairs by order within each row, so 전열 1 pairs with 후열 6 on P1 (numbers run on across the rows)', () => {
const pairs = pairEccentricities(model.blocks);
expect(pairs[0]).toMatchObject({ frontNo: 1, backNo: 6 });
});
it('a bearing left without a partner (the rows have different counts) is not paired', () => {
expect(pairEccentricities([block(1, '전열', 1), block(2, '전열', 3), block(6, '후열', 1.05)])).toHaveLength(1);
});
});
describe('일체 배치 선택 (승인 2026-10-26)', () => {
it('P1 allows 일체 because every pair is within 100 mm', () => {
expect(integratedCheck(model.blocks)).toEqual({ allowed: true, overLimit: [] });
});
it('one pair over the limit blocks 일체 and lists that pair', () => {
const r = integratedCheck([block(1, '전열', 1.0), block(1, '후열', 1.12)]);
expect(r.allowed).toBe(false);
expect(r.overLimit.map((p) => p.frontNo)).toEqual([1]);
});
it('no pairs at all does not allow 일체', () => {
expect(integratedCheck([]).allowed).toBe(false);
});
it('the model keeps 분리 for now and says so (B01); a 일체 that is not allowed is refused with B02', () => {
const input = loadP1();
const ok: Issue[] = [];
validatePier({ ...input, bearingLayout: '일체' }, model, ok);
expect(ok.map((i) => i.code)).toContain('B01');
const bad: Issue[] = [];
const fake = { ...model, blocks: [block(1, '전열', 1.0), block(1, '후열', 1.12)] } as unknown as typeof model;
validatePier({ ...input, bearingLayout: '일체' }, fake, bad);
expect(bad.map((i) => i.code)).toContain('B02');
});
});