Files
Pier_v1/tests/abut-shape.test.ts
T
ypshimandClaude Haiku 5.5 5af88a511e feat(abut): 교대 벽체·흉벽 삽도와 수축줄눈·흉벽 Level 입력 추가
- 벽체·흉벽 개략 단면 SVG(src/view/abut-section-svg.ts)를 형상 카드 맨 위에 추가. 치수 글자는 카드 입력값을 mm로 표시하고, 값을 바꾸면 다시 그림
- 형상 입력에 수축줄눈 개수(정수 0 이상)와 흉벽 상단 Level 좌·우(450·995, 계산 미사용·QA-4)를 추가. 기하 계산에는 쓰지 않음
- 테스트 619건 통과, 타입 검사·빌드 통과

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-03 14:57:38 +09:00

75 lines
3.9 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 { ABUT_SHAPE_DEFAULT, resolveShape, shapeIssues } from '../src/core/abut/shape';
describe('교대 형상 입력 (시안 ② 벽체·흉벽 · 확대기초·말뚝)', () => {
it('the defaults equal the spec values (wall +1.6 … −0.8, footing +2.8 … −1.8, piles 650 / 2000)', () => {
const r = resolveShape();
expect(r.wallFrontM).toBeCloseTo(1.6, 9);
expect(r.wallBackM).toBeCloseTo(-0.8, 9);
expect(r.parapetBackM).toBeCloseTo(-0.5, 9);
expect(r.footingFrontM).toBeCloseTo(2.8, 9);
expect(r.footingBackM).toBeCloseTo(-1.8, 9);
expect(r.pileEdgeM).toBeCloseTo(0.65, 9);
expect(shapeIssues({})).toEqual([]);
expect(ABUT_SHAPE_DEFAULT.wallThicknessMm).toBe(2400);
});
it('a changed wall thickness moves the back face (front − thickness)', () => {
expect(resolveShape({ wallThicknessMm: 2000 }).wallBackM).toBeCloseTo(-0.4, 9);
});
it('refuses a footing that does not cover the wall, and non-positive sizes', () => {
expect(shapeIssues({ footingBackMm: -500 }).join(' ')).toContain('뒷굽');
expect(shapeIssues({ footingFrontMm: 1000 }).join(' ')).toContain('앞굽');
expect(shapeIssues({ wallThicknessMm: 0 }).join(' ')).toContain('벽체 두께');
expect(shapeIssues({ pileSpacingMm: 0 }).join(' ')).toContain('말뚝');
});
});
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import * as XLSX from 'xlsx';
import { applyShapeEdit, SHAPE_EDIT_FIELDS } from '../src/core/abut/edit';
import { buildAbut } from '../src/core/abut/geometry';
import { buildMembers } from '../src/core/abut/members';
import { parseAbutments } from '../src/core/abut/parse';
const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url));
const rec = parseAbutments(XLSX.read(readFileSync(RAG), { type: 'buffer' })).abutments[0];
describe('형상 입력이 계산에 반영됨 (시안 ② 카드)', () => {
it('the card has one field per shape value, in the mockup order', () => {
expect(SHAPE_EDIT_FIELDS.map((f) => f.key)[0]).toBe('wallFrontMm');
expect(SHAPE_EDIT_FIELDS).toHaveLength(13);
});
it('the new drawing values do not change the geometry (the model ignores them)', () => {
expect(resolveShape({ shrinkJointCount: 3, parapetLevelLeftMm: 800 })).toEqual(resolveShape());
});
it('the shrink-joint count must be a whole number, not negative', () => {
expect(shapeIssues({ shrinkJointCount: 1.5 }).join(' ')).toContain('수축줄눈');
expect(shapeIssues({ shrinkJointCount: -1 }).join(' ')).toContain('수축줄눈');
expect(shapeIssues({ shrinkJointCount: 0 })).toEqual([]);
});
it('a footing that does not cover the wall is refused by the edit rule', () => {
expect(() => applyShapeEdit(rec, 'footingBackMm', -500)).toThrow('뒷굽');
expect(rec.shape).toBeUndefined();
});
it('a changed footing thickness moves the footing top used by the walls', () => {
const edited = applyShapeEdit(rec, 'footingThicknessMm', 1300);
const model = buildAbut({ name: 'A1', type: 'A1', seatEl: rec.seatEl, ip1: rec.ip1, ip2: rec.ip2, ip3: rec.ip3, bearings: rec.bearings.map((b) => ({ no: b.no, pos: b.pos, z: b.z, seatEl: b.seatEl })) });
const base = buildMembers(model, { bottomEl: rec.foundationBottomEl, pileLengthMm: rec.pileLengthMm });
const more = buildMembers(model, { bottomEl: rec.foundationBottomEl, pileLengthMm: rec.pileLengthMm }, edited.shape);
expect(more.footingTopEl - base.footingTopEl).toBeCloseTo(0.2, 6);
});
it('a changed pile spacing changes the number of piles per row', () => {
const model = buildAbut({ name: 'A1', type: 'A1', seatEl: rec.seatEl, ip1: rec.ip1, ip2: rec.ip2, ip3: rec.ip3, bearings: rec.bearings.map((b) => ({ no: b.no, pos: b.pos, z: b.z, seatEl: b.seatEl })) });
const wider = buildMembers(model, {}, { pileSpacingMm: 3000 });
expect(wider.piles[0].s.length).toBeLessThan(6);
});
});