Files
Pier_v1/src/core/bearing-table.ts
T
ypshimandClaude Haiku 5.5 1d31d2f3a9 feat(abut): 교대 형상·입력 패널과 교각 받침·솔플레이트 개편
- 교대 형상(src/core/abut, 3D 장면, 목록·카메라·맞춤)과 교대 입력 패널 추가
- 교각 받침 기준 카드 제거, 솔플레이트 규칙을 교각별 입력으로 변경
- 받침 ELEVATION·솔플레이트 TABLE을 모델값으로 연결
- 교대 입력값 카드: 형식 행에 시점·종점 표시, 제목을 "A1 입력값 [입력]"으로 변경
- 테스트 614건 통과, 타입 검사 통과

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-03 14:51:34 +09:00

158 lines
5.3 KiB
TypeScript
Raw 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.
/**
* 교량받침 표 (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),
};
}