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(/ { const { m, g } = group(); const box = new THREE.Box3().setFromObject((g.getObjectByName('piles') as THREE.Group).children[0]); expect(box.max.y).toBeCloseTo(m.footingBottomEl! + 0.1, 4); expect(box.min.y).toBeCloseTo(m.pileBottomEl!, 4); }); });