Files
Pier_v1/tests/abut-joints.test.ts
ypshimandClaude Opus 5.5 bff1502ecf fix(abut): 수축줄눈을 받침 사이 중간에 · 기본 개수 ceil(L/10) − 1 · 3D 선 표시
spec_abut §3 · §4.9 (QA-7 가정)와 맞지 않던 세 가지를 고친다.
- 위치: 등분점(1개면 L/2)이 아니라 가장 가까운 '받침 사이 중간'(= 교좌면 계단 경계). 이전에는 운곡교 교대
  5기 모두 줄눈이 가운데 받침 ③ 블럭을 지났다 (A1: 줄눈 6.099 · 받침 6.037). 이제 A1은 IP3에서 4.905 m
- 개수: 입력이 없으면 ceil(L/10) − 1 (길이에 따라 자동, 이전에는 1 고정). 입력 칸은 비우면 자동
- 3D: 벽체 전면 · 흉벽 전면 · 벽체 배면 · 흉벽 배면에 선으로 표시(저판은 연속이라 없음), '벽체·흉벽' 토글을 따름
- 공통 계산 core/abut/joints.ts를 ② 정면·배면 삽도, 교대 상세 요약, 입력 칸 안내, 3D가 함께 씀
- 테스트: 개수 규칙, A1 위치 = ②·③ 중간 = 계단 경계, 여러 개 · 받침 부족 시 등분, 3D 선 4면

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:23:04 +09:00

44 lines
2.4 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 * as THREE from 'three';
import { describe, expect, it } from 'vitest';
import { defaultJointCount, jointCount, jointPositions } from '../src/core/abut/joints';
import { buildAbut } from '../src/core/abut/geometry';
import { parseAbutments, toAbutInput } from '../src/core/abut/parse';
import { buildMembers } from '../src/core/abut/members';
import { buildAbutGroup } from '../src/view/abut-scene';
import { loadInputWorkbook } from './helpers';
// 교대 수축줄눈 (spec_abut §3 · §4.9, QA-7): count ceil(L/10) − 1, position = the middle between the nearest bearings.
const rec = parseAbutments(loadInputWorkbook()).abutments.find((a) => a.name === 'A1')!;
const model = buildAbut(toAbutInput(rec));
const bearingS = model.bearings.map((b) => b.s);
describe('교대 수축줄눈', () => {
it('count: ceil(L/10) − 1 by default (12.197 → 1, 20 → 1, 20.5 → 2, 9 → 0), a typed count wins', () => {
expect([12.197, 20, 20.5, 9].map(defaultJointCount)).toEqual([1, 1, 2, 0]);
expect(jointCount(12.197, 3)).toBe(3);
});
it('A1: the joint is not on bearing ③ (L/2) but in the middle between ② and ③, which is the 교좌면 step boundary', () => {
const [s] = jointPositions(model.length, bearingS);
const sorted = [...bearingS].sort((a, b) => a - b);
expect(s).toBeCloseTo((sorted[2] + sorted[3]) / 2, 9);
expect(Math.min(...bearingS.map((b) => Math.abs(b - s)))).toBeGreaterThan(0.6 + 0.6);
expect(model.stepBounds.some((x) => Math.abs(x - s) < 1e-3)).toBe(true);
});
it('several joints take distinct midpoints nearest to the even split; without enough bearings, the even split', () => {
const js = jointPositions(model.length, bearingS, 2);
expect(new Set(js.map((x) => x.toFixed(6))).size).toBe(2);
expect(jointPositions(30, [5], 2)).toEqual([10, 20]);
});
it('3D: one line on each of the four 벽체 · 흉벽 faces per joint, none in the 저판', () => {
const members = buildMembers(model, { bottomEl: rec.foundationBottomEl, pileLengthMm: rec.pileLengthMm });
const group = buildAbutGroup(model, members);
const lines = group.getObjectByName('joints')!.children[0] as THREE.LineSegments;
const pos = lines.geometry.getAttribute('position') as THREE.BufferAttribute;
expect(pos.count).toBe(4 * 2);
for (let i = 0; i < pos.count; i++) expect(pos.getY(i)).toBeGreaterThanOrEqual(members.footingTopEl - 1e-5);
});
});