/** * 교량받침 표 (2026-11-03, 모델값). Pure: the ELEVATION TABLE and the 솔플레이트 TABLE of a pier, every value from the * model. No drawing values are used (C안). Lengths in mm; EL.X in m. * * - EL.X: the mortar top = the Excel z (padTop, Q16). * - h1: block height (block top − block bottom). h2: mortar (pad top − block top). h3: bearing body (solplate rule). * - h4: solplate centre thickness T. E = h1 + h2 + h3 + h4. * - C1: bearing centre to the outer block edge along 교축. C2: between the two bearings of a pair, along 교축 (on the * front row). C3: block span of the pair along 교축 (front row). C4·C5: 교각축 margins (bearing-layout). C6: block depth * along 교각축. * - S1·S2: block rebar bars from the model (bearing-block): count and bar length. For 일체 the pair's bars sit on the front row. */ import type { PierModel } from './geometry'; import { bearingEdgeDistances, pairEccentricities } from './bearing-layout'; import { INTEGRATED_BLOCK } from './integrated-block'; import { bearingBlockRebar, type BarRow } from './rebar/bearing-block'; /** Depth of a 분리 bearing block along 교각축 (mm), the same value as the block rebar. */ export const BLOCK_DEPTH_MM = 1200; export interface ElevationRow { no: number; row: '전열' | '후열'; elx: number; /** 교좌면 EL (m): the block bottom. */ seatEl: number; h1: number; h2: number; h3: number; h4: number; E: number; C1: number; /** Front row only; null on the back row. */ C2: number | null; C3: number | null; C4: number; C5: number; C6: number; S1: BarSummary | null; S2: BarSummary | null; } export interface BarSummary { count: number; length: number; } export interface SolplateRow { no: number; /** 기울기 S (%), negative as drawn. */ S: number; L: number; W: number; T: number; T1: number; T2: number; /** Check: T1 − T2 against L·|S|/100 (mm). */ checkLhs: number; checkRhs: number; ok: boolean; } function barSummary(bars: BarRow[], name: 'S1' | 'S2'): BarSummary | null { const hit = bars.filter((x) => x.name === name); if (hit.length === 0) return null; return { count: hit.reduce((sum, x) => sum + x.count, 0), length: hit[0].length }; } export function elevationRows(m: PierModel): ElevationRow[] { const edges = bearingEdgeDistances(m); const rebar = bearingBlockRebar(m); const pairs = pairEccentricities(m.blocks); return [...m.blocks] .sort((a, b) => a.no - b.no) .map((b) => { const plan = b.plan; const h1 = Math.round((b.top - b.bottom) * 1000); const h2 = Math.round((b.padTop - b.top) * 1000); const edge = edges.find((e) => e.no === b.no)!; const ib = m.integratedBlocks.find((x) => (b.row === '전열' ? x.frontNo : x.backNo) === b.no); // C1: bearing centre to the outer edge of its block along 교축 (1200 block centred on the bearing, or the 2500 block). const C1 = ib ? Math.round(Math.abs(b.n - (b.n <= ib.n ? ib.n - INTEGRATED_BLOCK.length / 2000 : ib.n + INTEGRATED_BLOCK.length / 2000)) * 1000) : 600; const pair = pairs.find((p) => (b.row === '전열' ? p.frontNo : p.backNo) === b.no); const front = b.row === '전열'; const C2 = front && pair ? Math.round(Math.abs(m.blocks.find((x) => x.no === pair.backNo && x.row === '후열')!.n - b.n) * 1000) : null; const C3 = front && pair ? (ib ? INTEGRATED_BLOCK.length : (C2 ?? 0) + BLOCK_DEPTH_MM) : null; const bars = rebar.find((r) => r.no === b.no && r.row === b.row)?.bars ?? []; return { no: b.no, row: b.row, elx: b.padTop, seatEl: b.bottom, h1, h2, h3: plan.h3, h4: plan.T, E: h1 + h2 + plan.h3 + plan.T, C1, C2, C3, C4: Math.round(edge.leftMm), C5: Math.round(edge.rightMm), C6: BLOCK_DEPTH_MM, S1: barSummary(bars, 'S1'), S2: barSummary(bars, 'S2'), }; }); } export function solplateRows(m: PierModel): SolplateRow[] { return [...m.blocks] .sort((a, b) => a.no - b.no) .map((b) => { const p = b.plan; const checkLhs = p.T1 - p.T2; const checkRhs = (p.L * Math.abs(p.slopePct)) / 100; return { no: b.no, S: p.slopePct, L: p.L, W: p.W, T: p.T, T1: p.T1, T2: p.T2, checkLhs, checkRhs, ok: Math.abs(checkLhs - checkRhs) < 1e-6, }; }); } /** * S1·S2 of one pair along 교축 (mm, 2026-11-03): S1 = bearing centre to the coping edge on each outer side, S2 = between * the two bearing centres. The coping is centred on the axis with width B (model copingB), so S1 + S2 + S1 = B. */ export interface PairCopingSpans { S1Low: number; S2: number; S1High: number; copingWidth: number; } export function pairCopingSpans(m: PierModel, pairIndex: number): PairCopingSpans | null { const pair = pairEccentricities(m.blocks)[pairIndex]; if (!pair) return null; const f = m.blocks.find((b) => b.no === pair.frontNo && b.row === '전열')!; const k = m.blocks.find((b) => b.no === pair.backNo && b.row === '후열')!; const lo = Math.min(f.n, k.n); const hi = Math.max(f.n, k.n); const half = m.copingB / 2; return { S1Low: Math.round((lo + half) * 1000), S2: Math.round((hi - lo) * 1000), S1High: Math.round((half - hi) * 1000), copingWidth: Math.round(m.copingB * 1000), }; }