- PierInput.blockRebar (partial): cover top, side cover, first gap, spacing S1 and S2 (separate), leg length. Defaults are the P1 values, so P1 is unchanged. - S2 depth = top cover; S1 depth = top cover + S1/2 + S2/2 (H19: 69.1 at the default). - The model carries the settings; the bearing-block rebar, the 3D bars, and the summary use them. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
141 lines
6.0 KiB
TypeScript
141 lines
6.0 KiB
TypeScript
import type { PierModel } from '../geometry';
|
||
import { INTEGRATED_BLOCK, type IntegratedBlock } from '../integrated-block';
|
||
|
||
/**
|
||
* 받침블럭 철근 (spec §6.3 · plan C2), 배치 분리: one block per bearing, 1200 × 1200.
|
||
* The settings come from the input (승인 2026-10-26): S1 and S2 have their own spacing (따로 입력), the side cover c_s
|
||
* and the first-bar gap e_s are shared, and the S1 depth is the top cover + half of S1 + half of S2 (피복 + S1/2 + S2/2).
|
||
* S2 is the upper bar (depth = top cover); S1 is the lower bar.
|
||
*/
|
||
export interface BlockRebarSettings {
|
||
/** 상면 피복 (철근 중심까지, mm). */
|
||
coverTop: number;
|
||
/** 측면 피복 (S1 다리·S2 끝, mm). */
|
||
coverSide: number;
|
||
/** 첫 철근 최소 여유 (측면 피복 다음, mm). */
|
||
firstGap: number;
|
||
/** 간격 S1 (mm). */
|
||
spacingS1: number;
|
||
/** 간격 S2 (mm). */
|
||
spacingS2: number;
|
||
/** 다리 길이 (철근 중심에서 아래로, mm). */
|
||
legLength: number;
|
||
}
|
||
|
||
/** Defaults: the P1 values of 받침블럭철근.md (피복 50 · 여유 75 · @125 · 다리 700). */
|
||
export const DEFAULT_BLOCK_REBAR: BlockRebarSettings = {
|
||
coverTop: 50,
|
||
coverSide: 50,
|
||
firstGap: 75,
|
||
spacingS1: 125,
|
||
spacingS2: 125,
|
||
legLength: 700,
|
||
};
|
||
|
||
/** H19 bar diameter (mm) of both S1 and S2. */
|
||
const H19 = 19.1;
|
||
|
||
/** Depth of each bar centre below the block top (mm): S2 = top cover; S1 = top cover + S1/2 + S2/2. */
|
||
export function barDepths(s: BlockRebarSettings): { S1: number; S2: number } {
|
||
return { S2: s.coverTop, S1: s.coverTop + H19 / 2 + H19 / 2 };
|
||
}
|
||
|
||
export interface BarRow {
|
||
name: 'S1' | 'S2';
|
||
/** Bars along one block side. */
|
||
count: number;
|
||
spacing: number;
|
||
/** Position of the first bar from the block edge. */
|
||
firstEnd: number;
|
||
/** Straight length plus two legs (mm). */
|
||
length: number;
|
||
/** Coping embedment of the legs (mm): legLength − (block height − bar depth). */
|
||
embedment: number;
|
||
}
|
||
|
||
/** Bars along one side: available = dim − 2·(cover + first gap); gaps = floor(available / spacing). */
|
||
export function barsAcross(
|
||
dim: number = 1200,
|
||
spacing: number = DEFAULT_BLOCK_REBAR.spacingS2,
|
||
s: BlockRebarSettings = DEFAULT_BLOCK_REBAR,
|
||
): { count: number; spacing: number; firstEnd: number } {
|
||
const available = dim - 2 * (s.coverSide + s.firstGap);
|
||
const gaps = Math.floor(available / spacing);
|
||
const span = gaps * spacing;
|
||
const leftover = available - span;
|
||
return { count: gaps + 1, spacing, firstEnd: s.coverSide + s.firstGap + leftover / 2 };
|
||
}
|
||
|
||
/** Length of one bar (mm): the straight part a − 2·c_s plus two legs (2500 for the 1200 block with the defaults). */
|
||
export function barLength(dim: number = 1200, s: BlockRebarSettings = DEFAULT_BLOCK_REBAR): number {
|
||
return dim - 2 * s.coverSide + 2 * s.legLength;
|
||
}
|
||
|
||
/** Rebar of one block; blockHeight is the bearing block height in mm (block top − bottom, pad excluded). */
|
||
export function blockBars(blockHeight: number, dim: number = 1200, s: BlockRebarSettings = DEFAULT_BLOCK_REBAR): BarRow[] {
|
||
const depth = barDepths(s);
|
||
const length = barLength(dim, s);
|
||
return [
|
||
{ name: 'S1', ...barsAcross(dim, s.spacingS1, s), length, embedment: s.legLength - (blockHeight - depth.S1) },
|
||
{ name: 'S2', ...barsAcross(dim, s.spacingS2, s), length, embedment: s.legLength - (blockHeight - depth.S2) },
|
||
];
|
||
}
|
||
|
||
export interface BlockRebar {
|
||
no: number;
|
||
row: '전열' | '후열';
|
||
bars: BarRow[];
|
||
}
|
||
|
||
/**
|
||
* Rebar of one 일체 block (승인 2026-10-26, 받침블럭철근.md "일체 a 2500"): S2 runs along the 교각축 (1200) with the bars
|
||
* across the 2500 length (10 on the front half, 9 on the back half); its legs are measured from the top of the half it sits in.
|
||
* S1 runs along the 2500 length, across the 1200 depth; its depth is taken from the lower top.
|
||
*/
|
||
export function integratedBlockBars(ib: IntegratedBlock, s: BlockRebarSettings = DEFAULT_BLOCK_REBAR): BarRow[] {
|
||
const half = INTEGRATED_BLOCK.length / 2;
|
||
const depth = barDepths(s);
|
||
const s2 = barsAcross(INTEGRATED_BLOCK.length, s.spacingS2, s);
|
||
const frontCount = Array.from({ length: s2.count }, (_, k) => s2.firstEnd + k * s2.spacing).filter((x) => x <= half + 1e-9).length;
|
||
const backCount = s2.count - frontCount;
|
||
const hFront = (ib.topFront - ib.bottom) * 1000;
|
||
const hBack = (ib.topBack - ib.bottom) * 1000;
|
||
const legS2 = (h: number) => s.legLength - (h - depth.S2);
|
||
const s1 = barsAcross(INTEGRATED_BLOCK.depth, s.spacingS1, s);
|
||
const hLow = Math.min(hFront, hBack);
|
||
const rows: BarRow[] = [
|
||
{ name: 'S2', count: frontCount, spacing: s2.spacing, firstEnd: s2.firstEnd, length: barLength(INTEGRATED_BLOCK.depth, s), embedment: legS2(hFront) },
|
||
];
|
||
if (backCount > 0) rows.push({ name: 'S2', count: backCount, spacing: s2.spacing, firstEnd: s2.firstEnd, length: barLength(INTEGRATED_BLOCK.depth, s), embedment: legS2(hBack) });
|
||
rows.push({ name: 'S1', count: s1.count, spacing: s1.spacing, firstEnd: s1.firstEnd, length: barLength(INTEGRATED_BLOCK.length, s), embedment: s.legLength - (hLow - depth.S1) });
|
||
return rows;
|
||
}
|
||
|
||
/**
|
||
* Rebar of every block of a pier. The block height is the model's own block (coping top → block top, pad excluded),
|
||
* since the bar depths are measured from the block top. The settings come from the model (input).
|
||
*/
|
||
export function bearingBlockRebar(model: PierModel): BlockRebar[] {
|
||
const s = model.blockRebar;
|
||
// 일체 (승인 2026-10-26): one block per pair replaces the two per-bearing blocks.
|
||
if (model.integratedBlocks.length > 0) {
|
||
return model.integratedBlocks.map((ib) => ({ no: ib.frontNo, row: '전열', bars: integratedBlockBars(ib, s) }));
|
||
}
|
||
return model.blocks.map((b) => ({
|
||
no: b.no,
|
||
row: b.row,
|
||
bars: blockBars((b.top - b.bottom) * 1000, 1200, s),
|
||
}));
|
||
}
|
||
|
||
/** Total bars of one pier: bars per block × blocks, for S1 and S2 separately. */
|
||
export function totals(rebar: BlockRebar[]): { S1: number; S2: number } {
|
||
return rebar.reduce(
|
||
(sum, blk) => {
|
||
for (const bar of blk.bars) sum[bar.name] += bar.count;
|
||
return sum;
|
||
},
|
||
{ S1: 0, S2: 0 },
|
||
);
|
||
}
|