Files
Pier_v1/src/core/rebar/column.ts
T
ypshimandClaude Haiku 5.5 b3533b69df feat(rebar): rebar strength per 규격 prefix as inputs on ③ (D SD300, H SD400, S SD500)
fyH and fyS become a grade per prefix (D · H · S), chosen from SD300 · SD400 · SD500. The defaults are the P1 values, so
the P1 results do not change. Each bar takes its grade from its 규격 (fyOf). The 철근표 has a 강도 column. Older files
that stored fyH / fyS are read into the grade map. Only fck is still an assumed value (I02). Approved 2026-10-23.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-23 09:51:36 +09:00

180 lines
6.4 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 { DEFAULT_MATERIAL, developmentLength, fyOf, hookLength, Material, NOMINAL_DIAMETER, spliceLength } from './common';
import { layers } from './footing';
/**
* 기둥 철근 C4 (plan C4). Source: RAG/기둥철근 (1).md, 2026-10-22 최종 정리. Type-1 원형 중실 기둥.
* Lengths in mm, heights in mm from the 확대기초 상면 (up +).
*/
export interface ColumnInput {
/** 기둥 지름 D. */
D: number;
/** 기둥 높이 H_c (확대기초 상면 ~ 코핑 Level 하면). */
Hc: number;
/** 기둥 중심 위치의 코핑 높이 h_cop. */
hcop: number;
/** 기둥 면 ~ P1 중심 피복 c. */
c: number;
/** P1 위 끝의 코핑 상면 아래 깊이 e_P1. */
eP1: number;
/** 띠철근 구간 위 끝의 코핑 상면 아래 깊이 e_top. */
eTop: number;
/** 확대기초 깊이 H_f (for P4). */
Hf: number;
/** 주철근 원주 등분 수 n_div. */
nDiv: number;
/** 주철근 커플러 간격 (확대기초 상면부터). */
ctcCoupler: number;
/** P2 커플러를 P1보다 올리는 높이. */
dzCp2: number;
ctcP3: number;
ctcP5: number;
ctcP4: number;
/** P4 구간 깊이 비율 r_P4. */
rP4: number;
ctcP6: number;
}
/** 예제 운곡교 P1 (기둥철근.md §1.2 기본값). */
export const COLUMN_P1: ColumnInput = {
D: 2000,
Hc: 12703,
hcop: 2644,
c: 125,
eP1: 200,
eTop: 1623,
Hf: 2000,
nDiv: 46,
ctcCoupler: 3000,
dzCp2: 150,
ctcP3: 300,
ctcP5: 120,
ctcP4: 120,
rP4: 0.5,
ctcP6: 1800,
};
/** A bar in the 확대기초 top layer, height from the 확대기초 상면 (mm, negative = below). */
export interface TopBar {
name: string;
z: number;
d: number;
}
/**
* RB-C07 (기둥철근.md §6.1–6.2, 결정): a P4 that interferes with a 확대기초 top bar is deleted.
* Interference: |z_P4 − z_bar| < (d_P4 + d_bar)/2. Only the top layer (B5, B1) is checked; the assembly bars
* B11/B12 are not, by the 2026-10-22 decision (Q-C9 무시).
*/
export function deleteInterferingP4(p4: number[], topBars: TopBar[], dP4: number = NOMINAL_DIAMETER.H19): { kept: number[]; deleted: { z: number; bar: string }[] } {
const kept: number[] = [];
const deleted: { z: number; bar: string }[] = [];
for (const z of p4) {
const hit = topBars.find((b) => Math.abs(z - b.z) < (dP4 + b.d) / 2);
if (hit) deleted.push({ z, bar: hit.name });
else kept.push(z);
}
return { kept, deleted };
}
/** Top-layer bars of the footing as heights from its 상면 (B5 and B1, §4.2). */
export function footingTopBars(Hf: number): TopBar[] {
const z = layers(Hf);
return [
{ name: 'B5', z: z.B5 - Hf, d: NOMINAL_DIAMETER.S29 },
{ name: 'B1', z: z.B1 - Hf, d: NOMINAL_DIAMETER.S25 },
];
}
const dS32 = NOMINAL_DIAMETER.S32;
const dH19 = NOMINAL_DIAMETER.H19;
const dH16 = NOMINAL_DIAMETER.H16;
const round = (v: number, d = 1) => Math.round(v * 10 ** d) / 10 ** d;
export function columnRebar(x: ColumnInput = COLUMN_P1, m: Material = DEFAULT_MATERIAL) {
// P1 and P2 main bars (기둥철근.md §3)
const r1 = x.D / 2 - x.c;
const r2 = r1 - dS32;
const zTop1 = x.Hc + x.hcop - x.eP1;
const lD2 = developmentLength(dS32, fyOf(m, 'S32'), m.fck);
const zTop2 = x.Hc + lD2;
// Bottom of P1 and P2: 확대기초 하면 2단 (B7) 위 + (d_B7 + d_P1)/2 (기둥철근.md §3.2, the same for both).
const zBot = -x.Hf + layers(x.Hf).B7 + (NOMINAL_DIAMETER.S19 + dS32) / 2;
const couplerLevels = Array.from({ length: Math.max(0, Math.floor(zTop1 / x.ctcCoupler) - 1) }, (_, i) => (i + 1) * x.ctcCoupler);
// Pieces of each main bar between its couplers (기둥철근.md §3.2): the bottom piece carries the 90° hook (16.5·d = 525).
const p1Hook = Math.round(hookLength('main90', dS32));
const p1Points = [zBot, ...couplerLevels, zTop1];
const p1Pieces = p1Points.slice(1).map((z, i) => round(z - p1Points[i] + (i === 0 ? p1Hook : 0)));
const p2Points = [zBot, ...couplerLevels.map((z) => z + x.dzCp2), zTop2];
const p2Pieces = p2Points.slice(1).map((z, i) => round(z - p2Points[i]));
const couplersP1 = couplerLevels.length;
const mainLines = x.nDiv;
const mainP1 = mainLines * (couplersP1 + 1);
const mainP2 = mainLines * (couplersP1 + 1);
const couplers = couplersP1 * x.nDiv * 2;
// Ties (기둥철근.md §4–§6)
// Tie radius r_t = r_1 + (d_P1 + d_P3)/2 (기둥철근.md §4.1)
const tieRadius = r1 + (dS32 + dH19) / 2;
const zTopT = x.Hc + x.hcop - x.eTop;
const lEmin = Math.max(x.D, zTopT / 6, 450);
const n5 = Math.floor(lEmin / x.ctcP5);
const lE = Math.max(lEmin, n5 * x.ctcP5 + x.ctcP5 / 2);
const p3 = Array.from({ length: Math.floor((zTopT - lE) / x.ctcP3) + 1 }, (_, k) => lE + k * x.ctcP3);
const n5all = Math.floor(lE / x.ctcP5);
const p5 = Array.from({ length: n5all }, (_, j) => lE - (j + 1) * x.ctcP5);
const z5last = lE - n5all * x.ctcP5;
const m4 = Math.floor((z5last + x.rP4 * x.Hf) / x.ctcP4);
const p4All = Array.from({ length: m4 }, (_, j) => z5last - (j + 1) * x.ctcP4);
const p4Check = deleteInterferingP4(p4All, footingTopBars(x.Hf));
const p4 = p4Check.kept;
// P6 crossties (기둥철근.md §7): 4 bars per 단, from the first kept P4
const z4first = p4.length ? p4[0] : z5last;
const p6Levels: number[] = [];
for (let z = z4first; z <= x.Hc; z += x.ctcP6) p6Levels.push(z);
const p6Straight = 2 * tieRadius;
const p6Hook = hookLength('tie90', dH16);
const p6Length = p6Straight + 2 * Math.round(p6Hook); // 135 mm per hook, as in the document
// P3 lap: Q-C6 is open; recommended = 1.3·l_d of H19 with the material inputs.
const lapP3 = spliceLength(dH19, fyOf(m, 'H19'), m.fck);
const p3Length = 2 * Math.PI * tieRadius + lapP3;
const p5Length = 2 * Math.PI * tieRadius;
return {
p3Length: round(p3Length, 1),
p5Length: round(p5Length, 1),
lapP3: round(lapP3, 1),
r1: round(r1),
r2: round(r2),
angleStepDeg: 360 / x.nDiv,
zTop1: round(zTop1),
zTop2: round(zTop2),
zBot: round(zBot, 2),
p1Hook,
p1Pieces,
p2Pieces,
couplerP1: couplerLevels,
couplerP2: couplerLevels.map((z) => z + x.dzCp2),
mainP1,
mainP2,
couplers,
tieRadius: round(tieRadius, 2),
tieCircumference: round(2 * Math.PI * tieRadius, 1),
zTopT: round(zTopT),
lEmin: round(lEmin),
lE: round(lE),
p3,
p5,
p4,
p6Levels,
p6Count: p6Levels.length * 4,
p6Length: round(p6Length, 1),
p4SemicircleLength: round(Math.PI * tieRadius, 1),
p3Count: p3.length,
p5Count: p5.length,
p4Count: p4.length,
p4Deleted: p4Check.deleted,
};
}