- 벽체·흉벽 개략 단면 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>
75 lines
3.9 KiB
TypeScript
75 lines
3.9 KiB
TypeScript
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);
|
||
});
|
||
});
|