feat(bearing): 교각 솔플레이트를 2026-11-06 결정으로 (짝 받침 S · 열 T · 거더 하면 고정)
받침방위각_솔플레이트검토.md §3과 시안(Version 110–113)을 따른다 (사용자 결정 2026-11-06: 시안 적용). 11-03 C안(받침높이 H 고정, h3 = H − T, S 입력)을 대체한다. - S: 교량 전체(A1, P1 … Pn, A2) 거더 짝 받침의 z 차 / 수평거리. 없으면 교각 종단경사 입력 → 다른 열 같은 순번 값(가정, WS03) → 0 %(WS03). 받침별 직접 수정(수정됨) - 평면 L = A + 100, W = B + 100 (받침별 A × B, 기본 700 × 640 가정), h3 181 - T = 열마다 max(22, ⌈22 + L·|S|/2⌉), 열 T 직접 수정 가능(T2 < T_min → WS02). T1은 오르막 쪽(3D 쐐기의 두꺼운 끝도 그쪽), T2 반대쪽 - 거더 하면 고정: 블럭 상면 = z − (T − T_ref), T_ref 30 - 짝 받침은 불러오기·프로젝트 열기 때 교량 전체로 계산해 교각 생성에 넘김 - ② 카드: h3·T_ref·T_min·여유·열 T·종단경사 입력, 표에 짝 받침·S 근거·받침별 S/A/B 수정·검산 W08·검사 - P1 검증(A1과 함께): S −1.908 … −1.902 %, L 800 · W 740 · T 30 · T1 38 · T2 22, 블럭 높이 변화 없음 - 블럭·솔플레이트 테스트는 A1–P1 짝 문맥으로 실행 (짝이 없으면 S 0 · T 22 · 블럭 +8 mm) 교대(QA-15 C안)와 Pier 시트 종단경사 열 읽기는 아직 남음. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5fec2743cb
commit
a0b0929e13
19 files changed
+486
-109
No files matched your search
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 솔플레이트 (결정 2026-11-06, RAG/받침방위각_솔플레이트검토.md §3, spec.md §5.3, 시안 Version 110–113). Pure.
|
||||
* Replaces the 2026-11-03 C안 (받침높이 H 고정, h3 = H − T, S 입력) by the user's decision to follow the 시안:
|
||||
*
|
||||
* - 평면: L = A + 여유 (거더 방향 = 방위각), W = B + 여유 (여유 100). A × B per bearing; absent = 700 × 640 (P1 전열 값, 가정).
|
||||
* - S: 같은 거더 양끝 받침의 z 차 / 수평거리 (girderPairs). 없으면 교각 종단경사 입력 → 같은 구조물 다른 열의 같은 순번 값
|
||||
* ("가정", WS03) → 0 % (WS03). 받침별 직접 수정 = "수정됨".
|
||||
* - T: 열마다 max(T_min, ⌈max(T_min + L·|S|/2)⌉). 직접 고치면 "수정됨", T2 < T_min이면 WS02.
|
||||
* T1 = round(T + L·|S|/2) 오르막 쪽, T2 = round(T − L·|S|/2).
|
||||
* - 높이: 거더 하면 고정 — 블럭 상면 = z − (T − T_ref); 받침 본체 h3 고정 (181, P1 상세도).
|
||||
*/
|
||||
import { type BearingPair, pairOf } from './bearing-pair';
|
||||
|
||||
export interface SoleSettings {
|
||||
/** 받침 본체 높이 h3 (mm). */
|
||||
h3Mm?: number;
|
||||
/** T_ref: the solplate thickness the workbook z assumed (mm). */
|
||||
tRefMm?: number;
|
||||
/** T_min: the thin-end minimum (mm). */
|
||||
tMinMm?: number;
|
||||
/** 솔플 여유 L − A = W − B (mm). */
|
||||
marginMm?: number;
|
||||
/** 받침 평면 A (거더 방향) × B per bearing number (mm). */
|
||||
plates?: Record<string, { aMm: number; bMm: number }>;
|
||||
/** T per row, when typed (mm). Absent = 자동. */
|
||||
tByRow?: Partial<Record<string, number>>;
|
||||
/** S per bearing number, when typed (%). */
|
||||
slopePct?: Record<string, number>;
|
||||
}
|
||||
|
||||
export const SOLE_DEFAULTS = { h3Mm: 181, tRefMm: 30, tMinMm: 22, marginMm: 100, aMm: 700, bMm: 640 } as const;
|
||||
|
||||
export type SlopeSource = '계산' | '입력' | '가정' | '수정됨' | '없음';
|
||||
|
||||
export interface BearingSole {
|
||||
no: number;
|
||||
row: string;
|
||||
pair?: { structure: string; no: number };
|
||||
slopePct: number;
|
||||
slopeSource: SlopeSource;
|
||||
aMm: number;
|
||||
bMm: number;
|
||||
/** L along the girder, W across it (mm). */
|
||||
L: number;
|
||||
W: number;
|
||||
/** Row thickness T and whether it was typed; T1 on the 오르막 쪽, T2 on the other end (mm). */
|
||||
T: number;
|
||||
tTyped: boolean;
|
||||
T1: number;
|
||||
T2: number;
|
||||
/** This bearing's requirement T_min + L·|S|/2 (mm). */
|
||||
tReq: number;
|
||||
h3: number;
|
||||
/** Block top drop T − T_ref (mm, + = the block gets lower). */
|
||||
dropMm: number;
|
||||
/** Unit vector (E, N) towards the 오르막 쪽, or null when S = 0. */
|
||||
uphill: { e: number; n: number } | null;
|
||||
warnings: ('WS01' | 'WS02' | 'WS03' | 'WS04')[];
|
||||
}
|
||||
|
||||
export interface SoleBearing {
|
||||
no: number;
|
||||
row: string;
|
||||
azimuthDeg: number;
|
||||
}
|
||||
|
||||
const DEG = Math.PI / 180;
|
||||
|
||||
/**
|
||||
* Solplates of one structure. `pairs` are the girder pairs of the whole bridge (girderPairs over A1, P1 … Pn, A2);
|
||||
* `fallbackSlopePct` is the structure's 종단경사 input (%), when given.
|
||||
*/
|
||||
export function structureSoles(structure: string, bearings: SoleBearing[], settings: SoleSettings = {}, pairs: BearingPair[] = [], fallbackSlopePct?: number): BearingSole[] {
|
||||
const s = { ...SOLE_DEFAULTS, ...settings };
|
||||
const rows = [...new Set(bearings.map((b) => b.row))];
|
||||
const order = (b: SoleBearing) => bearings.filter((q) => q.row === b.row).indexOf(b);
|
||||
const paired = new Map(bearings.map((b) => [b.no, pairOf(pairs, structure, b.no)]));
|
||||
const pairSlope = (b: SoleBearing) => paired.get(b.no)?.slopePct;
|
||||
|
||||
const slopes = bearings.map((b) => {
|
||||
const typed = settings.slopePct?.[String(b.no)];
|
||||
if (typed !== undefined) return { slopePct: typed, source: '수정됨' as SlopeSource };
|
||||
const own = pairSlope(b);
|
||||
if (own !== undefined) return { slopePct: own, source: '계산' as SlopeSource };
|
||||
if (fallbackSlopePct !== undefined) return { slopePct: fallbackSlopePct, source: '입력' as SlopeSource };
|
||||
// The same position in another row that has a pair (시안: 후열 ⑥ ← 전열 ①).
|
||||
for (const other of rows.filter((r) => r !== b.row)) {
|
||||
const twin = bearings.filter((q) => q.row === other)[order(b)];
|
||||
const v = twin && pairSlope(twin);
|
||||
if (v !== undefined) return { slopePct: v, source: '가정' as SlopeSource };
|
||||
}
|
||||
return { slopePct: 0, source: '없음' as SlopeSource };
|
||||
});
|
||||
|
||||
const plate = (b: SoleBearing) => s.plates?.[String(b.no)] ?? { aMm: s.aMm, bMm: s.bMm };
|
||||
const req = bearings.map((b, i) => s.tMinMm + ((plate(b).aMm + s.marginMm) * Math.abs(slopes[i].slopePct)) / 200);
|
||||
const rowT = new Map(rows.map((r) => {
|
||||
const typed = settings.tByRow?.[r];
|
||||
const auto = Math.max(s.tMinMm, Math.ceil(Math.max(...bearings.map((b, i) => (b.row === r ? req[i] : -Infinity))) - 1e-9));
|
||||
return [r, { T: typed ?? auto, typed: typed !== undefined }];
|
||||
}));
|
||||
|
||||
return bearings.map((b, i) => {
|
||||
const { aMm, bMm } = plate(b);
|
||||
const L = aMm + s.marginMm, W = bMm + s.marginMm;
|
||||
const { slopePct, source } = slopes[i];
|
||||
const { T, typed } = rowT.get(b.row)!;
|
||||
const half = (L * Math.abs(slopePct)) / 200;
|
||||
const T1 = Math.round(T + half), T2 = Math.round(T - half);
|
||||
const pair = paired.get(b.no);
|
||||
const warnings: BearingSole['warnings'] = [...(pair?.warnings ?? [])];
|
||||
if (T2 < s.tMinMm) warnings.push('WS02');
|
||||
if (source === '가정' || source === '없음') warnings.push('WS03');
|
||||
// S runs along the structure order, which is the azimuth direction (girderPairs looks ahead along it).
|
||||
const dir = { e: Math.sin(b.azimuthDeg * DEG), n: Math.cos(b.azimuthDeg * DEG) };
|
||||
const sign = Math.sign(slopePct);
|
||||
return {
|
||||
no: b.no, row: b.row, pair: pair ? (pair.from.structure === structure ? pair.to : pair.from) : undefined,
|
||||
slopePct, slopeSource: source, aMm, bMm, L, W, T, tTyped: typed, T1, T2, tReq: req[i], h3: s.h3Mm,
|
||||
dropMm: T - s.tRefMm, uphill: sign ? { e: sign * dir.e, n: sign * dir.n } : null, warnings,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** Input problems of the settings, in Korean; empty when they can build solplates. */
|
||||
export function soleSettingsIssues(settings: SoleSettings | undefined): string[] {
|
||||
if (!settings) return [];
|
||||
const out: string[] = [];
|
||||
const positive = (v: unknown, label: string) => {
|
||||
if (v !== undefined && !(typeof v === 'number' && Number.isFinite(v) && v > 0)) out.push(`${label}은(는) 0보다 커야 합니다.`);
|
||||
};
|
||||
positive(settings.h3Mm, '받침 높이 h3');
|
||||
positive(settings.tRefMm, 'T_ref');
|
||||
positive(settings.tMinMm, 'T_min');
|
||||
if (settings.marginMm !== undefined && !(Number.isFinite(settings.marginMm) && settings.marginMm >= 0)) out.push('솔플 여유는 0 이상이어야 합니다.');
|
||||
for (const [no, p] of Object.entries(settings.plates ?? {})) { positive(p.aMm, `받침 ${no} A`); positive(p.bMm, `받침 ${no} B`); }
|
||||
for (const [row, t] of Object.entries(settings.tByRow ?? {})) positive(t, `${row} T`);
|
||||
for (const [no, v] of Object.entries(settings.slopePct ?? {})) if (!Number.isFinite(v)) out.push(`받침 ${no} S는 숫자여야 합니다.`);
|
||||
return out;
|
||||
}
|
||||
Reference in new issue
Block a user