Files
Pier_v1/tests/abut-footing.test.ts
T
ypshimandClaude Opus 5.5 372d759eb7 feat(abut): ② 확대기초 · 말뚝 카드를 시안대로 작성 (평면 삽도, 버림, 열별 본수)
- 선택: 평면 skew 평행사변형(기본) / 직사각(비활성, 다음 단계), 말뚝 원기둥 D600 / 강관 · PHC(비활성, 규칙 미정)
- 평면 삽도 (src/view/abut-footing-svg.ts): 저판, 벽체 윤곽, 교대축, 말뚝 3열(skew만큼 밀림), 열 위치 · 본수, 폭 치수
- 입력: 폭 4600, 두께 1100, 앞굽 1200 (뒷굽 자동 1000), 버림 여유 · 두께 100 / 100, 말뚝 지름 600 (QA-5), 연단 650, 본수 규칙 간격 2000
- 열 위치 "650 · 2000 · 1300 · 650" 입력 (전면 연단 자동, 합 = 폭 검사), 열별 말뚝 본수 표 − / + (0 = 본수 규칙)
- 자동 값: 뒷굽, 길이(교좌면 길이), 말뚝 길이(①에서 수정), 선단 EL, 본수 규칙 문구. 시안 메모
- 3D: 교대 버림을 저판 아래에 그림 (부재 표시 "버림" 토글 적용), 말뚝 지름 입력 반영, 말뚝 열을 skew만큼 s 방향으로 밀어 배치
- 예전 저장 파일의 앞굽 끝 · 뒷굽 끝은 폭 · 앞굽으로 바꿔 읽음
- 테스트 추가 · 갱신. 전체 661건 통과, 타입 검사 · 빌드 통과. 브라우저 시각 확인은 미실시

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 08:46:58 +09:00

60 lines
3.1 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 { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import * as XLSX from 'xlsx';
import * as THREE from 'three';
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 { abutSkewK, buildAbutGroup } from '../src/view/abut-scene';
import { abutFootingPlanSvg } from '../src/view/abut-footing-svg';
const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url));
const rec = parseAbutments(XLSX.read(readFileSync(RAG), { type: 'buffer' })).abutments[0];
const input = toAbutInput(rec);
const model = buildAbut(input);
const group = (changes = {}) => {
const m = buildMembers(model, { bottomEl: input.foundationBottomEl, pileLengthMm: input.pileLengthMm }, changes);
return { m, g: buildAbutGroup(model, m, buildWings(model, m)) };
};
describe('교대 확대기초 · 말뚝 (시안 ②, 3D)', () => {
it('draws the 버림 under the 저판: 100 thick, 100 wider on each side', () => {
const { m, g } = group();
const blind = new THREE.Box3().setFromObject(g.getObjectByName('blinding') as THREE.Object3D);
const foot = new THREE.Box3().setFromObject(g.getObjectByName('footing') as THREE.Object3D);
expect(blind.max.y).toBeCloseTo(m.footingBottomEl!, 4); // float32 vertices
expect(blind.max.y - blind.min.y).toBeCloseTo(0.1, 4);
expect(foot.min.z - blind.min.z).toBeCloseTo(0.1, 4);
expect(blind.max.z - foot.max.z).toBeCloseTo(0.1, 4);
});
it('no 버림 when its thickness is 0', () => {
expect(group({ blindingThicknessMm: 0 }).g.getObjectByName('blinding')).toBeUndefined();
});
it('the piles follow the 지름 input and shift with the skew row by row', () => {
const { m, g } = group({ pileDiameterMm: 800 });
const piles = (g.getObjectByName('piles') as THREE.Group).children;
expect(piles).toHaveLength(18);
const box = new THREE.Box3().setFromObject(piles[0]);
expect(box.max.x - box.min.x).toBeCloseTo(0.8, 2);
const k = abutSkewK(model);
const row = m.piles[0];
expect((box.min.x + box.max.x) / 2).toBeCloseTo(row.s[0] + k * row.t, 6);
});
it('평면 삽도: the rows, their t and counts, and the width line', () => {
const { m } = group();
const svg = abutFootingPlanSvg({
lengthM: model.length, skewK: abutSkewK(model), footingFrontM: m.shape.footingFrontM, footingBackM: m.shape.footingBackM,
wallFrontM: m.shape.wallFrontM, wallBackM: m.shape.wallBackM,
rows: m.piles.map((r, i) => ({ t: r.t, s: r.s, name: ['3열 · 배면', '2열', '1열 · 전면'][i] })),
pileDiameterM: m.shape.pileDiameterM, rowDimsMm: [650, 2000, 1300, 650], footingWidthMm: 4600,
});
for (const s of ['1열 · 전면 · t +2150 · 6본', '2열 · t +850 · 6본', '3열 · 배면 · t −1150 · 6본', '4600 = 뒷굽쪽 650 · 2000 · 1300 · 650 전면', '교대축']) expect(svg).toContain(s);
expect(svg.match(/<circle /g)).toHaveLength(18);
});
});