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>
This commit is contained in:
1 parent
1fda705b75
commit
bff1502ecf
8 files changed
+158
-13
No files matched your search
@@ -0,0 +1,43 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -20,7 +20,8 @@ describe('교대 상세 (삽도 항목, spec_abut §10.1)', () => {
|
||||
it('the piles, joints, 접속슬래브 width and 받침블럭 height match the spec: 18 piles, tip 19.295, 1 joint, 11253 mm, 125 mm', () => {
|
||||
expect(rows.get('말뚝 (열 × 본수)')).toBe('3 × 6 = 18 본');
|
||||
expect(rows.get('말뚝 선단 EL')).toBe('19.295 m');
|
||||
expect(rows.get('수축줄눈 (ceil(L/10) − 1)')).toBe('1 개');
|
||||
// 1 joint (ceil(12.197/10) − 1), moved from L/2 (on bearing ③) to the middle between ② and ③ (2026-11-06).
|
||||
expect(rows.get('수축줄눈 (기본 ceil(L/10) − 1, 받침 사이 중간)')).toMatch(/^1 개 · IP3에서 4\.90[45] m$/);
|
||||
// 11.2535 m exactly (450 / sin Δ + 20 per wing); rounding to mm gives 11253 or 11254.
|
||||
expect(Math.abs(parseInt(String(rows.get('접속슬래브 폭')), 10) - 11253)).toBeLessThanOrEqual(1);
|
||||
expect(rows.get('받침블럭 높이 (패드 포함)')).toBe('125 mm');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { jointPositions } from '../src/core/abut/joints';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as XLSX from 'xlsx';
|
||||
@@ -26,7 +27,7 @@ const d: AbutWallDrawing = {
|
||||
footingFrontM: m.shape.footingFrontM,
|
||||
footingBackM: m.shape.footingBackM,
|
||||
bracketHeightM: m.shape.bracketHeightM,
|
||||
shrinkJointCount: 1,
|
||||
shrinkJointsS: jointPositions(model.length, model.bearings.map((b) => b.s)),
|
||||
bracketWidthMm: 300,
|
||||
parapetThicknessMm: 500,
|
||||
seatWidthMm: 1600,
|
||||
@@ -50,7 +51,7 @@ describe('교대 벽체·흉벽 삽도 (시안 ② 벽체·흉벽 카드)', () =
|
||||
});
|
||||
|
||||
it('the 수축줄눈 count follows the input', () => {
|
||||
const svg = abutWallElevationSvg({ ...d, shrinkJointCount: 2 });
|
||||
const svg = abutWallElevationSvg({ ...d, shrinkJointsS: jointPositions(model.length, model.bearings.map((b) => b.s), 2) });
|
||||
expect(svg).toContain('수축줄눈 2');
|
||||
expect(svg.match(/stroke-dasharray="5 3"/g)).toHaveLength(2);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user