- 위 연직 헌치(흉벽 배면)와 아래 연직 헌치(벽체 배면) 삭제. 관련 코드와 Haunch 타입 제거 - 저판 상면 헌치는 유지 - 테스트 갱신. 전체 637건 통과, 타입 검사·빌드 통과. 브라우저 시각 확인은 미실시 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
150 lines
6.4 KiB
TypeScript
150 lines
6.4 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import * as XLSX from 'xlsx';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { buildAbut, type AbutInput } from '../src/core/abut/geometry';
|
|
import { parseAbutments, toAbutInput } from '../src/core/abut/parse';
|
|
import { buildMembers } from '../src/core/abut/members';
|
|
import { buildWings, WING_LENGTH_M } from '../src/core/abut/wings';
|
|
|
|
const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url));
|
|
const a1Input = toAbutInput(parseAbutments(XLSX.read(readFileSync(RAG), { type: 'buffer' })).abutments[0]);
|
|
const SKEW = 95 + 2 / 60 + 52 / 3600;
|
|
const GIRDER = 75 + 56 / 60 + 55.9 / 3600;
|
|
|
|
// A2 합성 예제 (spec §10.2): ① 쪽이 IP1 끝.
|
|
const a2Input: AbutInput = {
|
|
name: 'A2',
|
|
type: 'A2',
|
|
seatEl: 44.367,
|
|
ip1: { e: 179161.0318, n: 259932.3259 },
|
|
ip2: { e: 179161.0417, n: 259932.3276 },
|
|
ip3: { e: 179173.0573, n: 259930.2859 },
|
|
bearings: [
|
|
{ no: 1, pos: { e: 179161.9111, n: 259931.422 }, z: 44.492 },
|
|
{ no: 2, pos: { e: 179164.3854, n: 259931.0021 }, z: 44.563 },
|
|
{ no: 3, pos: { e: 179166.8597, n: 259930.5821 }, z: 44.634 },
|
|
{ no: 4, pos: { e: 179169.334, n: 259930.1621 }, z: 44.705 },
|
|
{ no: 5, pos: { e: 179171.8084, n: 259929.7421 }, z: 44.776 },
|
|
],
|
|
skewDeg: SKEW,
|
|
slopeI: -0.0191,
|
|
girderAzimuthDeg: GIRDER,
|
|
foundationBottomEl: 40.895,
|
|
pileLengthMm: 21600,
|
|
};
|
|
|
|
describe('교대 A2 합성 · 날개벽 (AA3b, spec_abut §10.2)', () => {
|
|
const model = buildAbut(a2Input);
|
|
const members = buildMembers(model, { bottomEl: 40.895, pileLengthMm: 21600 });
|
|
const [left, right] = buildWings(model, members);
|
|
|
|
it('좌 날개벽 (① 쪽, IP1 끝): top 46.838 at the start, 46.714 at the end, bottom end 45.214', () => {
|
|
expect(left.side).toBe('left');
|
|
expect(left.sEnd).toBeCloseTo(0, 9);
|
|
expect(left.topStartEl).toBeCloseTo(46.838, 2);
|
|
expect(left.topEndEl).toBeCloseTo(46.714, 2);
|
|
expect(left.bottomEndEl).toBeCloseTo(45.214, 2);
|
|
});
|
|
|
|
it('우 날개벽 (⑤ 쪽, IP3 끝): top 47.183 → 47.059, bottom end 45.559', () => {
|
|
expect(right.side).toBe('right');
|
|
expect(right.sEnd).toBeCloseTo(model.length, 9);
|
|
expect(right.topStartEl).toBeCloseTo(47.183, 2);
|
|
expect(right.topEndEl).toBeCloseTo(47.059, 2);
|
|
expect(right.bottomEndEl).toBeCloseTo(45.559, 2);
|
|
});
|
|
|
|
it('the wing is 6.5 m long along the wing line and 0.45 m thick inward', () => {
|
|
const [p0, p1] = left.corners;
|
|
expect(Math.hypot(p1.e - p0.e, p1.n - p0.n)).toBeCloseTo(WING_LENGTH_M, 6);
|
|
const [, , q2, q3] = left.corners;
|
|
expect(Math.hypot(q2.e - p1.e, q2.n - p1.n)).toBeCloseTo(0.45, 6);
|
|
expect(Math.hypot(q3.e - p0.e, q3.n - p0.n)).toBeCloseTo(0.45, 6);
|
|
});
|
|
|
|
it('the wing starts at the axis end with the wing direction on the 배면 side (AA2 line 75.32°)', () => {
|
|
const [p0, p1] = left.corners;
|
|
const deg = (Math.atan2(p1.n - p0.n, p1.e - p0.e) * 180) / Math.PI;
|
|
const cad = ((deg % 360) + 360) % 360;
|
|
expect(cad).toBeCloseTo(75.32, 1);
|
|
});
|
|
|
|
it('a 단축형 wing bottom sits at the footing top (41.995)', () => {
|
|
const short = buildWings(model, members, { left: 'short', right: 'none' });
|
|
expect(short).toHaveLength(1);
|
|
expect(short[0].bottomEndEl).toBeCloseTo(41.995, 3);
|
|
});
|
|
|
|
it('no wing when the side is 없음', () => {
|
|
expect(buildWings(model, members, { left: 'none', right: 'none' })).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('교대 A1 날개벽 (운곡교P1입력.xlsx)', () => {
|
|
const model = buildAbut(a1Input);
|
|
const members = buildMembers(model, { bottomEl: a1Input.foundationBottomEl, pileLengthMm: a1Input.pileLengthMm });
|
|
const wings = buildWings(model, members);
|
|
|
|
it('A1 has two general wings; ① 쪽 wing starts at the IP3 end (the bearing ① is near IP3)', () => {
|
|
expect(wings.map((w) => w.side)).toEqual(['left', 'right']);
|
|
const left = wings[0];
|
|
expect(left.sEnd).toBeCloseTo(model.length, 9);
|
|
expect(left.topStartEl).toBeCloseTo(46.835, 2);
|
|
// A1 날개벽 경사는 +1.91 % (벽체에서 멀어질수록 높아짐): 끝이 0.124 m 높다.
|
|
expect(left.topEndEl - left.topStartEl).toBeCloseTo(0.124, 2);
|
|
});
|
|
});
|
|
|
|
describe('교대 날개벽 하단 선 · 헌치 (AA3c, spec_abut §4.7)', () => {
|
|
const a1 = buildAbut(a1Input);
|
|
const a1m = buildMembers(a1, { bottomEl: a1Input.foundationBottomEl, pileLengthMm: a1Input.pileLengthMm });
|
|
const [a1Left] = buildWings(a1, a1m);
|
|
const a2 = buildAbut(a2Input);
|
|
const a2m = buildMembers(a2, { bottomEl: 40.895, pileLengthMm: 21600 });
|
|
const [a2Left] = buildWings(a2, a2m);
|
|
|
|
it('뒷굽 끝까지의 날개벽 거리 1.807 m (단축형 표와 같다)', () => {
|
|
expect(a1Left.heelDistanceM).toBeCloseTo(1.807, 2);
|
|
expect(a2Left.heelDistanceM).toBeCloseTo(1.807, 2);
|
|
});
|
|
|
|
it('하단선: 뒷굽 끝까지 저판 상면 41.995, 그 밖은 단부 하단까지 직선 (A2 좌: 45.214)', () => {
|
|
const line = a2Left.bottomLine;
|
|
expect(line[0].el).toBeCloseTo(41.995, 3);
|
|
expect(line[1].dM).toBeCloseTo(1.807, 2);
|
|
expect(line[1].el).toBeCloseTo(41.995, 3);
|
|
expect(line[2].el).toBeCloseTo(45.214, 2);
|
|
});
|
|
|
|
it('the 저판 상면 haunch runs from the 벽체 배면 to the 뒷굽 on the 저판 상면; the 뒷굽 haunch is gone', () => {
|
|
const f = a2Left.footingHaunch!;
|
|
expect(f.el).toBeCloseTo(41.995, 3);
|
|
expect(f.dMTo).toBeGreaterThan(f.dMFrom);
|
|
expect((a2Left as { heelHaunch?: unknown }).heelHaunch).toBeUndefined();
|
|
});
|
|
|
|
});
|
|
|
|
describe('날개벽 수평 단 (QA-12 초안 적용, spec_abut §4.7)', () => {
|
|
const a2 = buildAbut(a2Input);
|
|
const a2m = buildMembers(a2, { bottomEl: 40.895, pileLengthMm: 21600 });
|
|
const [a2Left] = buildWings(a2, a2m);
|
|
|
|
it('등단면 down to 1.5 m below the wing end, then 변화단면1 to the seat EL and 변화단면2 to the footing top', () => {
|
|
const [equal, v1, v2] = a2Left.bands;
|
|
expect(equal.name).toBe('등단면');
|
|
expect(equal.zBottomEl).toBeCloseTo(45.214, 2);
|
|
expect(v1.name).toBe('변화단면1');
|
|
expect(v1.zBottomEl).toBeCloseTo(44.367, 3);
|
|
expect(v2.name).toBe('변화단면2');
|
|
expect(v2.zBottomEl).toBeCloseTo(41.995, 3);
|
|
});
|
|
|
|
it('bands are contiguous from the wing top to the footing top', () => {
|
|
const b = buildWings(a2, a2m)[0].bands;
|
|
for (let i = 1; i < b.length; i++) expect(b[i].zTopEl).toBeCloseTo(b[i - 1].zBottomEl, 6);
|
|
expect(b[b.length - 1].zBottomEl).toBeCloseTo(41.995, 3);
|
|
});
|
|
});
|