feat(bearing): 솔플레이트 표에 '방위각 검사 (″)' 열 (Q-SH4)

교각·교대 솔플레이트 상세 TABLE에 받침별로 엑셀 방위각과 짝 받침 방향의 차이(초)를 보인다.
10″ 이하 "≤ 10″", 넘으면 WS01, 짝이 없으면 "— (짝 없음)".
- BearingSole.azimuthCheckSec (짝의 from/to 쪽에 맞는 값), 표 행 azimuthSec
- 테스트: P1 전열 ≤ 0.5″ · 후열 없음, A1 ≤ 0.5″, P1 ③ 방위각 75°59′ 오타 → 약 87″ · WS01 그 받침만
- 브라우저 확인(운곡교 완도·광주 데이터): P1-광주 전열 짝 A1-광주 0.0–0.3″, 후열 짝 P2-광주 0.0–0.1″

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Opus 5.5 committed 2026-11-06 15:39:42 +09:00
1 parent 54c855ab1a
commit 3bd2340609
7 files changed
+41 -8

No files matched your search

+2
View File
@@ -53,6 +53,8 @@ describe('교대 받침 카드 (솔플레이트 결정 2026-11-06, h3 = 받침
expect(rows.map((r) => r.S.toFixed(3))).toEqual(['-1.908', '-1.906', '-1.904', '-1.904', '-1.902']);
expect(rows.every((r) => r.source === '계산' && r.pair?.startsWith('P1-'))).toBe(true);
expect(rows[0].check).toBe('16 ≈ 15.3 일치');
expect(rows.every((r) => r.azimuthSec !== undefined && r.azimuthSec <= 0.5)).toBe(true);
expect(abutSolplateRows(model, rule, H, { slopeI: rec.slopeI })[0].azimuthSec).toBeUndefined();
});
it('without a pair the 교대 종단경사 i (−0.0191) is used: S −1.91 % (입력), T 30', () => {
+3
View File
@@ -59,6 +59,9 @@ describe('솔플레이트 TABLE (모델값, 2026-11-03)', () => {
expect(rows[0].checkRhs).toBeCloseTo(15.265, 2);
// 후열 ⑥: no pair (P2 not loaded) → the front twin ① value, 가정 + WS03.
expect(rows[5]).toMatchObject({ source: '가정', pair: undefined, T: 30 });
// 방위각 검사 (″, Q-SH4): front row against the A1 direction ≤ 0.5″, back row has no pair.
expect(rows.slice(0, 5).every((r) => r.azimuthSec !== undefined && r.azimuthSec <= 0.5)).toBe(true);
expect(rows[5].azimuthSec).toBeUndefined();
expect(rows[5].warnings).toContain('WS03');
});
});
+16 -1
View File
@@ -3,11 +3,15 @@ import * as THREE from 'three';
import { buildPier } from '../src/core/geometry';
import { solplateGeometry } from '../src/core/solplate';
import { buildPierScene } from '../src/view/scene';
import { loadP1, p1Pairs } from './helpers';
import { loadInputWorkbook, loadP1, p1Pairs } from './helpers';
import { parseAbutments } from '../src/core/abut/parse';
import { girderPairs } from '../src/core/bearing-pair';
// Bridge context (A1 ↔ P1 girder pairs) as in the app, so the solplate slope is computed (2026-11-06).
const P1CTX = { pairs: p1Pairs() };
const FRONT = { 1: 3, 2: 3, 3: 3, 4: 3, 5: 3 };
const a1 = parseAbutments(loadInputWorkbook()).abutments.find((a) => a.name === 'A1')!;
const A1S = { name: 'A1', bearings: a1.bearings.map((b) => ({ no: b.no, x: b.pos.e, y: b.pos.n, z: b.z, azimuthDeg: b.azimuthDeg! })) };
describe('솔플레이트 모델과 3D (결정 2026-11-06, Q-SH1–9)', () => {
it('every bearing: h3 181, L 800 × W 740 (A 700 × B 640 + 100), T 30, T1 38 / T2 22', () => {
@@ -27,6 +31,17 @@ describe('솔플레이트 모델과 3D (결정 2026-11-06, Q-SH1–9)', () => {
expect(m.blocks.find((b) => b.no === 6)!.plan.T).toBe(30);
});
it('방위각 검사: P1 ③ azimuth typed as 75°59′ (CAD) shows ≈ 87″ and WS01 on that bearing only', () => {
const p1 = loadP1();
const typo = { ...p1, bearings: p1.bearings.map((b) => (b.no === 3 ? { ...b, azimuthDeg: 90 - (75 + 59 / 60) } : b)) };
const pairs = girderPairs([A1S, { name: 'P1', bearings: typo.bearings.map((b) => ({ no: b.no, x: b.x, y: b.y, z: b.z, azimuthDeg: b.azimuthDeg })) }]);
const m = buildPier(typo, { pairs });
const s3 = m.soles.find((s) => s.no === 3)!;
expect(s3.azimuthCheckSec!).toBeGreaterThan(80);
expect(s3.warnings).toContain('WS01');
expect(m.soles.filter((s) => s.no !== 3 && s.row === '전열').every((s) => s.azimuthCheckSec! <= 0.5 && !s.warnings.includes('WS01'))).toBe(true);
});
it('WS02: T typed 30 at S 3 % leaves T2 18 < 22', () => {
const m = buildPier({ ...loadP1(), sole: { slopePct: FRONT, tByRow: { 전열: 30 } } }, P1CTX);
const s = m.soles.find((x) => x.no === 1)!;