Files
Pier_v1/tests/abut-wing-card.test.ts

76 lines
4.8 KiB
TypeScript
Raw Permalink 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 { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import * as XLSX from 'xlsx';
import { describe, expect, it } from 'vitest';
import { buildAbut } from '../src/core/abut/geometry';
import { parseAbutments, toAbutInput } from '../src/core/abut/parse';
import { buildMembers } from '../src/core/abut/members';
import { buildWings } from '../src/core/abut/wings';
import { abutPlanOutline } from '../src/view/abut-scene';
import { abutWingCornerSvg, abutWingPlanSvg, abutWingSideSvg } from '../src/view/abut-wing-svg';
const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url));
const rec = parseAbutments(XLSX.read(readFileSync(RAG), { type: 'buffer' })).abutments[0];
const build = (r = rec) => {
const model = buildAbut(toAbutInput(r));
const members = buildMembers(model, { bottomEl: r.foundationBottomEl, pileLengthMm: r.pileLengthMm }, r.shape);
return { model, members, wings: buildWings(model, members, r.wings, r.wingDims) };
};
describe('날개벽 치수 입력 (측별) 과 연동', () => {
it('defaults: 6500 · 450 · 1500 · haunch 300 on both sides; 단축형 runs to the 뒷굽 끝 (1.807 m)', () => {
const { wings } = build();
for (const w of wings) expect([w.lengthM, w.thicknessM, w.endHeightM, w.haunchM]).toEqual([6.5, 0.45, 1.5, 0.3]);
const short = build({ ...rec, wings: { left: 'short', right: 'short' } }).wings[0];
expect(short.lengthM).toBeCloseTo(short.heelDistanceM, 3);
expect(short.heelDistanceM).toBeCloseTo(1.807, 3);
});
it('a thicker wing narrows the 접속슬래브 (slab width = L − Σ(t/|sin Δ| + 20))', () => {
const base = build().model.wing!.slabWidthMm;
const thick = build({ ...rec, wingDims: { left: { thicknessMm: 550 }, right: { thicknessMm: 550 } } });
expect(base - thick.model.wing!.slabWidthMm).toBeGreaterThan(195); // 2 × 100 / |sin Δ| ≈ 200.8
expect(thick.wings[0].thicknessM).toBeCloseTo(0.55, 9);
});
it('haunch 0 drops the 연직 헌치 and the 저판 상면 헌치 of that wing', () => {
const w = build({ ...rec, wingDims: { left: { haunchMm: 0 }, right: {} } }).wings.find((x) => x.side === 'left')!;
expect(w.haunches).toHaveLength(0);
expect(w.footingHaunch).toBeUndefined();
});
});
describe('날개벽 삽도 (시안 ② 날개벽 카드)', () => {
it('평면: both wings, the slab between them and its width', () => {
const { model, members, wings } = build();
const svg = abutWingPlanSvg({
lengthM: model.length, outline: abutPlanOutline(model, members, wings), leftLabel: '좌 · 일반형', rightLabel: '우 · 일반형',
leftSEnd: wings.find((w) => w.side === 'left')!.sEnd, slabWidthM: model.wing!.slabWidthMm / 1000,
});
for (const s of ['좌 · 일반형', '우 · 일반형', `접속슬래브 폭 ${(model.wing!.slabWidthMm / 1000).toFixed(3)} m`]) expect(svg).toContain(s);
expect(svg.match(/<polygon /g)).toHaveLength(4); // slab, wall, two wings
});
it('측면: the bands, the length, the end height, the 뒷굽 끝, and the 벽체 · 흉벽 · 브라켓 · 저판 with EL. labels', () => {
const { members, wings } = build();
const w = wings[0];
const svg = abutWingSideSvg({
name: '좌 날개벽', form: w.form, lengthM: w.lengthM, endHeightM: w.endHeightM, heelDistanceM: w.heelDistanceM,
topLine: w.topLine, bottomLine: w.bottomLine, bands: w.bands, seatEl: rec.seatEl, footingTopEl: members.footingTopEl,
parapetTopEl: w.topStartEl, bracketHeightM: members.shape.bracketHeightM, wallBackM: members.shape.wallBackM,
parapetBackM: members.shape.parapetBackM, wallFrontM: members.shape.wallFrontM, footingFrontM: members.shape.footingFrontM,
footingBackM: members.shape.footingBackM, footingThicknessM: members.shape.footingThicknessM,
});
for (const s of ['6500 (교좌면 끝에서, 날개벽 축 방향)', '1500', '등단면', '변화단면1', '변화단면2', '뒷굽 끝 1.807 m', '교좌면 EL.44.367', 'EL.41.995', '기초 하단 EL.40.895']) expect(svg).toContain(s);
expect(svg).toMatch(/흉벽 상단 EL\.4[67]\.\d{3}/);
expect(svg).toMatch(/브라켓 상면 EL\.4[56]\.\d{3}/);
expect(svg.match(/<polygon /g)).toHaveLength(5); // wing, 저판, 벽체, 브라켓 단차, 흉벽
expect(svg.match(/stroke-width="0.8"/g)).toHaveLength(15); // 치수선 3개 (6500, 1500, 뒷굽 끝) × (보조선 2 + 치수선 1 + 틱 2)
});
it('모서리: haunch, bracket, slab chamfer and gaps from the inputs', () => {
const svg = abutWingCornerSvg({ parapetMm: 500, bracketMm: 300, wingThicknessMm: 450, haunchMm: 300, slabGapWingMm: 20, slabGapParapetMm: 5, chamferMm: 300 });
for (const s of ['헌치 300 × 300', '브라켓 300 (슬래브 아래)', '슬래브 모따기 300 × 300', '날개벽과 20 · 흉벽과 5', '흉벽 500', '날개벽 450']) expect(svg).toContain(s);
});
});