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
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* 거더 짝 받침 · 종단경사 S · 솔플레이트 열 두께 (결정 2026-11-06, RAG/받침방위각_솔플레이트검토.md §3, Q-SH1·4·8·9).
|
||||
* Pure. Not wired into the model yet: the height rule (Q-SH7, 블럭 상면 = z − (T − T_ref)) conflicts with the 2026-11-03
|
||||
* C안 already in use (받침높이 H 고정, h3 = H − T, src/core/solplate-rule.ts), so only the parts both rules need live here.
|
||||
* Pure. Used by src/core/sole-plate.ts (pier solplates, 2026-11-06) through src/core/bridge-pairs.ts.
|
||||
*
|
||||
* Frame: (E, N) in m as in the model (x = E, y = N). Azimuth: the model's 진북 방위각 (deg, clockwise from north), i.e. the
|
||||
* workbook CAD angle θ_CAD converted by 90 − θ_CAD. A girder at azimuth θ runs along (sin θ, cos θ).
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* 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 { SlopeSource } from './sole-plate';
|
||||
import type { PierModel } from './geometry';
|
||||
import { bearingEdgeDistances, pairEccentricities } from './bearing-layout';
|
||||
import { INTEGRATED_BLOCK } from './integrated-block';
|
||||
@@ -54,12 +55,24 @@ export interface SolplateRow {
|
||||
T: number;
|
||||
T1: number;
|
||||
T2: number;
|
||||
/** Check: T1 − T2 against L·|S|/100 (mm). */
|
||||
/** Check W08: T1 − T2 against L·|S|/100 (mm), within 1 mm (T1 and T2 are rounded to whole mm). */
|
||||
checkLhs: number;
|
||||
checkRhs: number;
|
||||
ok: boolean;
|
||||
/** 짝 받침 (같은 거더), e.g. "A1-①"; absent when none (2026-11-06). */
|
||||
pair?: string;
|
||||
/** Where S comes from: 계산 (짝) / 입력 (종단경사) / 가정 (다른 열) / 수정됨 / 없음. */
|
||||
source: SlopeSource;
|
||||
/** T typed by the user (수정됨) rather than automatic. */
|
||||
tTyped: boolean;
|
||||
/** 받침 평면 A × B (mm). */
|
||||
A: number;
|
||||
B: number;
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
const CIRCLED_NO = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬', '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳'];
|
||||
|
||||
function barSummary(bars: BarRow[], name: 'S1' | 'S2'): BarSummary | null {
|
||||
const hit = bars.filter((x) => x.name === name);
|
||||
if (hit.length === 0) return null;
|
||||
@@ -112,9 +125,12 @@ export function solplateRows(m: PierModel): SolplateRow[] {
|
||||
.sort((a, b) => a.no - b.no)
|
||||
.map((b) => {
|
||||
const p = b.plan;
|
||||
const s = m.soles.find((x) => x.no === b.no)!;
|
||||
const checkLhs = p.T1 - p.T2;
|
||||
const checkRhs = (p.L * Math.abs(p.slopePct)) / 100;
|
||||
return {
|
||||
pair: s.pair ? `${s.pair.structure}-${CIRCLED_NO[s.pair.no - 1] ?? s.pair.no}` : undefined,
|
||||
source: s.slopeSource, tTyped: s.tTyped, A: s.aMm, B: s.bMm, warnings: s.warnings,
|
||||
no: b.no,
|
||||
S: p.slopePct,
|
||||
L: p.L,
|
||||
@@ -124,7 +140,7 @@ export function solplateRows(m: PierModel): SolplateRow[] {
|
||||
T2: p.T2,
|
||||
checkLhs,
|
||||
checkRhs,
|
||||
ok: Math.abs(checkLhs - checkRhs) < 1e-6,
|
||||
ok: Math.abs(checkLhs - checkRhs) <= 1 + 1e-9,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Girder pairs over the whole bridge (A1, P1 … Pn, A2 — structureOrder) for the solplate slope S (결정 2026-11-06, Q-SH1).
|
||||
* Pure. Bearing coordinates and z cannot be edited on the screens, so the pairs change only when structures are loaded.
|
||||
*/
|
||||
import type { AbutRecord } from './abut/parse';
|
||||
import { structureOrder } from './abut/order';
|
||||
import { type BearingPair, girderPairs, type PairStructure } from './bearing-pair';
|
||||
import type { PierInput } from './types';
|
||||
|
||||
export function bridgePairs(piers: Pick<PierInput, 'name' | 'bearings'>[], abuts: AbutRecord[]): BearingPair[] {
|
||||
const byPier = new Map(piers.map((p) => [p.name, p]));
|
||||
const byAbut = new Map(abuts.map((a) => [a.name, a]));
|
||||
const structures: PairStructure[] = structureOrder(abuts, piers.map((p) => p.name)).map((ref) => {
|
||||
if (ref.kind === 'pier') {
|
||||
const p = byPier.get(ref.name)!;
|
||||
return { name: p.name, bearings: p.bearings.filter((b) => Number.isFinite(b.azimuthDeg)).map((b) => ({ no: b.no, x: b.x, y: b.y, z: b.z, azimuthDeg: b.azimuthDeg })) };
|
||||
}
|
||||
const a = byAbut.get(ref.name)!;
|
||||
return { name: a.name, bearings: a.bearings.filter((b) => b.azimuthDeg !== undefined).map((b) => ({ no: b.no, x: b.pos.e, y: b.pos.n, z: b.z, azimuthDeg: b.azimuthDeg! })) };
|
||||
});
|
||||
return girderPairs(structures);
|
||||
}
|
||||
+28
-7
@@ -6,7 +6,8 @@ import { type ColumnRebarSettings, DEFAULT_COLUMN_REBAR } from './rebar/column-s
|
||||
import { type FootingRebarSettings, DEFAULT_FOOTING_REBAR } from './rebar/footing-settings';
|
||||
import { layoutText, resolveRowCounts, rowLayout, type PileGeometry } from './piles';
|
||||
import type { PierInput, Row, SolplatePlan } from './types';
|
||||
import { SOLPLATE_RULE_DEFAULT, solplatePlan } from './solplate-rule';
|
||||
import type { BearingPair } from './bearing-pair';
|
||||
import { type BearingSole, structureSoles } from './sole-plate';
|
||||
import { type CopingGeom, P1_COPING_GEOM } from './coping-geom';
|
||||
import { castShape } from './cast-in-place-shape';
|
||||
|
||||
@@ -87,7 +88,7 @@ export interface BlockModel {
|
||||
padTop: number;
|
||||
/** Skew (deg, −90…90] of the bearing row against the perpendicular of the stretch axis; the block sides follow the row. */
|
||||
rowSkewDeg: number;
|
||||
/** Solplate of this bearing (C안, 2026-11-03): the pier's rule, the same for every bearing. */
|
||||
/** Solplate of this bearing (2026-11-06): S from the girder pair, T per row, T1 on the 오르막 쪽. */
|
||||
plan: SolplatePlan;
|
||||
/** Block bottom from the 두부보 상면 slope (before any 교좌면 EL input), for the check against the input (2026-11-03). */
|
||||
seatCalc: number;
|
||||
@@ -221,6 +222,8 @@ export interface PierModel {
|
||||
};
|
||||
/** Dedicated rebar source; never substitute footing-anchored column inputs. */
|
||||
castInput?: PierInput;
|
||||
/** Solplate of every bearing with its S source, pair and warnings (2026-11-06), for the ② card and checks. */
|
||||
soles: BearingSole[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +248,10 @@ export function solveIp4(ip1: Vec2, ip2: Vec2, ip3: Vec2, angleDeg: number): Vec
|
||||
* Each object carries its stretch k and a local s measured from that stretch's base (k = 0: BP1, k = 1: IP4).
|
||||
* A bent coping (IP4 angle ≠ 0) puts each footing in its stretch's frame; footings that would cross the kink are not combined.
|
||||
*/
|
||||
export function buildPier(input: PierInput): PierModel {
|
||||
/** Bridge context for one pier: the girder pairs over A1, P1 … Pn, A2 (girderPairs), for the solplate slope S. */
|
||||
export interface PierContext { pairs?: BearingPair[] }
|
||||
|
||||
export function buildPier(input: PierInput, ctx: PierContext = {}): PierModel {
|
||||
const ip = (name: string) => {
|
||||
const p = input.ips.find((q) => q.name === name);
|
||||
if (!p) throw new Error(`IP 누락: ${name}`);
|
||||
@@ -455,19 +461,33 @@ export function buildPier(input: PierInput): PierModel {
|
||||
const blocks: BlockModel[] = [];
|
||||
const bearingAz = new Map(input.bearings.map((r) => [r.no, r.azimuthDeg]));
|
||||
const skewOf = (no: number, k: number) => rowSkewDeg(bearingAz.get(no) ?? NaN, axis.segments[k].u);
|
||||
// Solplate of every bearing from the pier's rule (C안, 2026-11-03): the same plan for each bearing.
|
||||
const plan = solplatePlan(input.solplate ?? SOLPLATE_RULE_DEFAULT);
|
||||
// Solplate of every bearing (결정 2026-11-06): S from the girder pair (ctx.pairs over the whole bridge), else the
|
||||
// 종단경사 input, else the other row; T per row; the girder underside stays, so the block top drops by T − T_ref.
|
||||
const soles = new Map(structureSoles(input.name, input.bearings.map((b) => ({ no: b.no, row: b.row, azimuthDeg: b.azimuthDeg })),
|
||||
input.sole, ctx.pairs ?? [], input.longitudinalSlope === undefined ? undefined : input.longitudinalSlope * 100).map((s) => [s.no, s]));
|
||||
const planOf = (no: number, k: number, skewDeg: number): SolplatePlan => {
|
||||
const s = soles.get(no)!;
|
||||
// The block's c axis in (E, N): (−sin d)·u + (cos d)·nrm; T1 goes to the end of c that points uphill.
|
||||
const { u, nrm } = axis.segments[k];
|
||||
const d = (skewDeg * Math.PI) / 180;
|
||||
const c = { e: -Math.sin(d) * u.e + Math.cos(d) * nrm.e, n: -Math.sin(d) * u.n + Math.cos(d) * nrm.n };
|
||||
const t1Side = s.uphill && c.e * s.uphill.e + c.n * s.uphill.n > 0 ? 1 : -1;
|
||||
return { L: s.L, W: s.W, slopePct: s.slopePct, T: s.T, T1: s.T1, T2: s.T2, h3: s.h3, t1Side };
|
||||
};
|
||||
const dropOf = (no: number) => soles.get(no)!.dropMm / 1000;
|
||||
// 교좌면 EL (2026-11-03): when the input gives it, the block bottom is that EL; otherwise the 두부보 상면 slope.
|
||||
const seatOf = new Map(input.bearings.map((r) => [r.no, r.seatEl]));
|
||||
frontPlaced.forEach((b) => {
|
||||
const seatCalc = segmentTop(segments, b.g);
|
||||
const bottom = seatOf.get(b.no) ?? seatCalc;
|
||||
blocks.push({ no: b.no, row: '전열', k: b.k, s: b.s, g: b.g, n: b.n, bottom, top: b.z - DIM.padThickness, padTop: b.z, rowSkewDeg: skewOf(b.no, b.k), plan, seatCalc });
|
||||
const skew = skewOf(b.no, b.k), drop = dropOf(b.no);
|
||||
blocks.push({ no: b.no, row: '전열', k: b.k, s: b.s, g: b.g, n: b.n, bottom, top: b.z - DIM.padThickness - drop, padTop: b.z - drop, rowSkewDeg: skew, plan: planOf(b.no, b.k, skew), seatCalc });
|
||||
});
|
||||
backPlaced.forEach((b) => {
|
||||
const seatCalc = b.z - h2;
|
||||
const bottom = seatOf.get(b.no) ?? seatCalc;
|
||||
blocks.push({ no: b.no, row: '후열', k: b.k, s: b.s, g: b.g, n: b.n, bottom, top: b.z - DIM.padThickness, padTop: b.z, rowSkewDeg: skewOf(b.no, b.k), plan, seatCalc });
|
||||
const skew = skewOf(b.no, b.k), drop = dropOf(b.no);
|
||||
blocks.push({ no: b.no, row: '후열', k: b.k, s: b.s, g: b.g, n: b.n, bottom, top: b.z - DIM.padThickness - drop, padTop: b.z - drop, rowSkewDeg: skew, plan: planOf(b.no, b.k, skew), seatCalc });
|
||||
});
|
||||
|
||||
const blockHeightFront = blocks.find((b) => b.row === '전열')!.top - blocks.find((b) => b.row === '전열')!.bottom;
|
||||
@@ -526,6 +546,7 @@ export function buildPier(input: PierInput): PierModel {
|
||||
blockHeightBack,
|
||||
frontBackDiffSpread,
|
||||
},
|
||||
soles: [...soles.values()],
|
||||
...(input.foundationType === '현장타설말뚝' ? { castInput: { ...input } } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 솔플레이트 기하 (승인 2026-10-23, C안 2026-11-03). Pure: the solplate of one bearing, from its block (position, rowSkew)
|
||||
* and the pier's solplate plan. The plan is L across the 교각축 직각 (n) and W along the 교축 (s). The top tapers across L:
|
||||
* the thick end T1 is at the (−) side of the 교각축 직각 direction (n = −L/2), the thin end T2 at (+) (n = +L/2).
|
||||
* 솔플레이트 기하 (승인 2026-10-23, 2026-11-06 결정). Pure: the solplate of one bearing, from its block (position, rowSkew)
|
||||
* and the bearing's solplate plan. L runs along the block's c axis (the girder, by the azimuth) and W along a. The top
|
||||
* tapers across L: the thick end T1 is on the 오르막 쪽 (c = t1Side·L/2), the thin end T2 at the other end.
|
||||
* The underside is the bearing top (block top EL.X + h3). Lengths in m, as in the model.
|
||||
*/
|
||||
import { blockFramePoint, type BlockModel } from './geometry';
|
||||
@@ -29,7 +29,9 @@ export function solplateGeometry(block: BlockModel, plan: SolplatePlan): Solplat
|
||||
const T1 = plan.T1 / 1000;
|
||||
const T2 = plan.T2 / 1000;
|
||||
const bottom = block.padTop + plan.h3 / 1000;
|
||||
const thickAt = (c: number) => T1 + ((T2 - T1) * (c + L / 2)) / L;
|
||||
// T1 at c = t1Side·L/2 (the 오르막 쪽), T2 at the other end.
|
||||
const side = plan.t1Side ?? -1;
|
||||
const thickAt = (c: number) => T1 + ((T2 - T1) * (side * -c + L / 2)) / L;
|
||||
const corners: [number, number, number][] = [];
|
||||
for (const a of [-W / 2, W / 2]) {
|
||||
for (const c of [-L / 2, L / 2]) {
|
||||
|
||||
+10
-3
@@ -1,3 +1,4 @@
|
||||
import type { SoleSettings } from './sole-plate';
|
||||
import type { CopingRebarSettings, TopSpliceSettings } from './rebar/coping-settings';
|
||||
import type { ColumnRebarSettings } from './rebar/column-settings';
|
||||
import type { FootingRebarSettings } from './rebar/footing-settings';
|
||||
@@ -62,8 +63,12 @@ export interface PierInput {
|
||||
pileThicknessMm?: number;
|
||||
/** 말뚝 종류 (규격 목록): 강관 (default) or PHC. */
|
||||
pileKind?: '강관' | 'PHC';
|
||||
/** 솔플레이트 규칙 of this pier (C안, 2026-11-03). Absent = the default (211 · 1000 × 1100 · −0.5 %). */
|
||||
/** Obsolete C안 (2026-11-03) rule: kept so that old project files still open; no longer used (replaced by `sole`, 2026-11-06). */
|
||||
solplate?: SolplateRule;
|
||||
/** 솔플레이트 입력 (결정 2026-11-06, 시안): h3, T_ref, T_min, 여유, 받침별 A × B, 열 T·받침 S 직접 수정. Absent = 기본값. */
|
||||
sole?: SoleSettings;
|
||||
/** 교각 종단경사 (소수, (−) = 교량진행방향으로 낮아짐; Pier 시트 "종단경사" 열, Q-SH2). 짝 받침이 없을 때 S에 쓴다. */
|
||||
longitudinalSlope?: number;
|
||||
/** 지반·지층 (승인 2026-10-26): 위치, 지하수위 고려와 EL, 지층별 행 (위에서 아래, 행의 EL = 그 층의 하단 EL).
|
||||
* Absent = 육상, 지하수위 없음, 지층 행 없음. */
|
||||
groundPosition?: GroundPosition;
|
||||
@@ -109,15 +114,17 @@ export interface SolplateRule {
|
||||
slopePct: number;
|
||||
}
|
||||
|
||||
/** The solplate every bearing of a pier gets from its rule (mm): T is the centre thickness, h3 = H − T. */
|
||||
/** The solplate of one bearing (mm): T is the row thickness, T1 the 오르막 쪽 end, h3 the bearing body (2026-11-06). */
|
||||
export interface SolplatePlan {
|
||||
L: number;
|
||||
W: number;
|
||||
slopePct: number;
|
||||
T: number;
|
||||
/** Thickness at the (−) side (T1) and the (+) side (T2) of the 교각축 직각 direction. */
|
||||
/** Thickness at the 오르막 쪽 (T1) and the other end (T2). */
|
||||
T1: number;
|
||||
T2: number;
|
||||
/** Side of the block's c axis (L direction) where T1 sits: −1 = c = −L/2, +1 = c = +L/2. Absent = −1. */
|
||||
t1Side?: -1 | 1;
|
||||
/** Bearing body height h3 (mm) between the mortar top and the solplate underside. */
|
||||
h3: number;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user