Files
Pier_v1/src/core/rebar/bearing-block.ts
T
ypshimandClaude Haiku 5.5 e1497176ea feat(rebar): block rebar settings as input (S1·S2 spacing separate, S1 depth from the cover and bar diameters)
- 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>
2026-10-26 14:08:58 +09:00

141 lines
6.0 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.
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 },
);
}