feat(rebar): add downward 90° hooks (135 mm) to P6 crossties in 3D rebar model
This commit is contained in:
1 parent
4aa8dbb3cb
commit
a2317caf6c
15 files changed
+740
-133
No files matched your search
+41
-19
@@ -1,7 +1,7 @@
|
||||
import type { PierModel } from '../geometry';
|
||||
import { bearingBlockRebar, totals } from './bearing-block';
|
||||
import { barLength, bearingBlockRebar, totals } from './bearing-block';
|
||||
import { bottomPiece, topBarLength, widthLayout } from './coping-main';
|
||||
import { shearBars, shearBarsMulti, sideRows, C14_COUNTS, c14Total, STEP_BAR_COUNTS, shearULength, SIDE_LENGTHS, STEP_LENGTHS, capLength, PIECES_PER_POSITION } from './coping';
|
||||
import { shearBars, shearBarsMulti, copingSideBars, SIDE_HOOK_LEG, c14Bars, c14Length, stairBars, stepDevLength, undersideAt, shearULength, capLength, PIECES_PER_POSITION } from './coping';
|
||||
import { columnRebar, COLUMN_P1 } from './column';
|
||||
import { bottomBars, crossBars, sideBars, assemblyBars, shearB9 } from './footing';
|
||||
import { DEFAULT_MATERIAL, isAssumedMaterial, Material, splitPieces } from './common';
|
||||
@@ -98,14 +98,14 @@ interface FootingBar {
|
||||
}
|
||||
|
||||
/** The footing bars of one footing (B1–B12 and B9), with the notes used in the 철근표. */
|
||||
function footingBarList(Lf: number, Wf: number, Hf: number): FootingBar[] {
|
||||
function footingBarList(Lf: number, Wf: number, Hf: number, columnX: number[]): FootingBar[] {
|
||||
const out: FootingBar[] = [];
|
||||
for (const b of bottomBars(Lf, Wf, Hf)) out.push({ ...b, note: '교각축' });
|
||||
for (const b of crossBars(Lf, Wf, Hf)) out.push({ ...b, note: '교각축 직각' });
|
||||
for (const b of sideBars(Lf, Wf)) out.push({ ...b, note: '측면 수평' });
|
||||
for (const b of assemblyBars(Lf, Wf)) out.push({ ...b, note: '조립 수평' });
|
||||
const b9 = shearB9(Lf, Wf, Hf);
|
||||
out.push({ name: 'B9', size: 'H16', count: b9.count, length: b9.length, note: `전단 @500 엇갈림, ${b9.rows}행 (위치·갈고리는 Q-B18·B19 제안)` });
|
||||
for (const b of sideBars(Lf, Wf, Hf)) out.push({ ...b, note: '측면 수평' });
|
||||
for (const b of assemblyBars(Lf, Wf, Hf)) out.push({ ...b, note: '조립 수평' });
|
||||
const b9 = shearB9(Lf, Wf, Hf, columnX);
|
||||
out.push({ name: 'B9', size: 'H16', count: b9.count, length: b9.length, note: `전단 @500 엇갈림, ${b9.rows}행, 기둥 구역 제외 (위치·갈고리는 Q-B18·B19 규칙 적용)` });
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -121,9 +121,20 @@ export function rebarSummary(model: PierModel, m: Material = DEFAULT_MATERIAL):
|
||||
const B = model.copingB * 1000;
|
||||
|
||||
// Bearing blocks (§6.3 · plan C2)
|
||||
const blk = totals(bearingBlockRebar(model));
|
||||
rows.push({ member: 'bearing', name: 'S1', size: 'H19', count: blk.S1, length: 2500, note: '블럭 배치 분리, 블럭당 8본' });
|
||||
rows.push({ member: 'bearing', name: 'S2', size: 'H19', count: blk.S2, length: 2500, note: '블럭 배치 분리, 블럭당 8본' });
|
||||
// Embedment in the coping (받침블럭철근.md §4): l_leg − (block height − bar depth), with the model's block heights.
|
||||
const blocks = bearingBlockRebar(model);
|
||||
const blk = totals(blocks);
|
||||
const embedments = (name: 'S1' | 'S2') => blocks.flatMap((b) => b.bars.filter((r) => r.name === name).map((r) => r.embedment));
|
||||
const emb1 = embedments('S1');
|
||||
const emb2 = embedments('S2');
|
||||
const range = (xs: number[]) => `${Math.round(Math.min(...xs))}–${Math.round(Math.max(...xs))}`;
|
||||
const anchorage = stepDevLength(m); // RB-S04: the embedment must reach the H19 development length
|
||||
const shortest = Math.min(...emb1, ...emb2);
|
||||
if (shortest < anchorage) {
|
||||
warnings.push({ code: 'RB-S04', message: `받침블럭 철근 묻힘 ${Math.round(shortest)} mm < 정착길이 ${anchorage} mm (코핑 안 묻힘 부족)`, target: 'bearing' });
|
||||
}
|
||||
rows.push({ member: 'bearing', name: 'S1', size: 'H19', count: blk.S1, length: barLength(), note: `블럭 배치 분리, 블럭당 8본 · 묻힘 ${range(emb1)} (블럭 상면 − 69.1)` });
|
||||
rows.push({ member: 'bearing', name: 'S2', size: 'H19', count: blk.S2, length: barLength(), note: `블럭 배치 분리, 블럭당 8본 · 묻힘 ${range(emb2)} (블럭 상면 − 50)` });
|
||||
|
||||
// Coping (코핑철근.md §5–§8)
|
||||
const width = widthLayout(B);
|
||||
@@ -157,17 +168,26 @@ export function rebarSummary(model: PierModel, m: Material = DEFAULT_MATERIAL):
|
||||
if (nCol > 1) {
|
||||
warnings.push({ code: 'RB-M01', message: `다주형 ${nCol}기둥: C14는 T형 기준으로 두었습니다 (기둥마다 반복 규칙 미정, Q-K11).`, target: 'coping' });
|
||||
}
|
||||
const side = sideRows(41.288, 42.538);
|
||||
rows.push({ member: 'coping', name: 'C12', size: 'H25', count: side.c12.length, length: null, note: `측면, 경사 하면에서 끊김, 일자 ${SIDE_LENGTHS.C12[0]}–${SIDE_LENGTHS.C12[4]}` });
|
||||
rows.push({ member: 'coping', name: 'C13', size: 'H25', count: side.c13.length, length: SIDE_LENGTHS.C13, note: '측면, 끝면까지' });
|
||||
rows.push({ member: 'coping', name: 'C14', size: 'H19', count: c14Total(), length: STEP_LENGTHS.C14, note: `위 ${C14_COUNTS.upperLines} 줄 ${C14_COUNTS.upperPerLine}본씩, 아래 ${C14_COUNTS.lower.join(' · ')} (합 ${c14Total()})` });
|
||||
rows.push({ member: 'coping', name: 'C15', size: 'H19', count: STEP_BAR_COUNTS.C15, length: STEP_LENGTHS.C15, note: '계단부, 가장 높은 구간 (거꾸로 U)' });
|
||||
rows.push({ member: 'coping', name: 'C16', size: 'H19', count: STEP_BAR_COUNTS.C16, length: STEP_LENGTHS.C16[0], note: `계단부, 3·4구간 (${STEP_LENGTHS.C16[0]} / ${STEP_LENGTHS.C16[1]})` });
|
||||
// Per row: two 일자 (side faces) and two ㄷ (one at each end), so 4 bars per row (코핑철근.md §6.2).
|
||||
const side = copingSideBars(model.copingL * 1000, 41.288, 42.538);
|
||||
const hook = model.copingB * 1000 - 2 * 127 + 2 * SIDE_HOOK_LEG; // ㄷ body across the width + two legs
|
||||
const c12Range = side.c12.length ? `${Math.round(side.c12[0].straight)}–${Math.round(side.c12[side.c12.length - 1].straight)}` : '—';
|
||||
rows.push({ member: 'coping', name: 'C12', size: 'H25', count: 4 * side.c12.length, length: null, note: `측면, 경사 하면에서 끊김, 일자 ${c12Range} (${2 * side.c12.length}) · ㄷ ${Math.round(hook)} (${2 * side.c12.length})` });
|
||||
rows.push({ member: 'coping', name: 'C13', size: 'H25', count: 4 * side.c13.length, length: side.c13.length ? side.c13[0].straight : null, note: `측면, 끝면까지, 일자 (${2 * side.c13.length}) · ㄷ ${Math.round(hook)} (${2 * side.c13.length})` });
|
||||
// C14 / C15 / C16 are derived from the model (코핑철근.md §7–8); the document's 38 / 11 / 22 and 3852 / 4070 / 4069 are checked in the tests.
|
||||
const L14 = model.copingL * 1000;
|
||||
const c14 = c14Bars(L14, model.copingRefTop, [...side.c12, ...side.c13], (x) => undersideAt(L14, x, model.endBottom, model.levelBottom));
|
||||
const lineCount = (line: number) => c14.filter((b) => b.line === line).length;
|
||||
const omitted = c14.filter((b) => !b.legs).length;
|
||||
rows.push({ member: 'coping', name: 'C14', size: 'H19', count: c14.length, length: c14Length(model.copingB * 1000), note: `위 2 줄 ${lineCount(1)} · ${lineCount(2)}본씩, 아래 ${[3, 4, 5, 6].map(lineCount).join(' · ')} (합 ${c14.length}) · 연직 발 생략 ${omitted}개 (하면 밖)` });
|
||||
const stairs = stairBars(model.copingL * 1000, model.copingB * 1000, model.copingRefTop);
|
||||
rows.push({ member: 'coping', name: 'C15', size: 'H19', count: stairs.c15.length, length: stairs.c15[0].length, note: '계단부, 가장 높은 구간 (거꾸로 U)' });
|
||||
rows.push({ member: 'coping', name: 'C16', size: 'H19', count: stairs.c16.length, length: stairs.c16[0].length, note: `계단부, 3·4구간 (${Math.round(stairs.c16[0].length)} / ${Math.round(stairs.c16[stairs.c16.length - 1].length)})` });
|
||||
|
||||
// Column (기둥철근.md · plan C4), P1 defaults
|
||||
const col = columnRebar(COLUMN_P1, m);
|
||||
rows.push({ member: 'column', name: 'P1', size: 'S32', count: col.mainP1, length: null, note: '외측 r 875, 마디 4697.5 + 갈고리 525 / 3000 ×3 / 3147 (코핑 밖 마디는 기둥철근.md §3.2)' });
|
||||
rows.push({ member: 'column', name: 'P2', size: 'S32', count: col.mainP2, length: null, note: '내측 r 843.2, 마디 4847.5 / 3000 ×3 / 2389' });
|
||||
rows.push({ member: 'column', name: 'P1', size: 'S32', count: col.mainP1, length: null, note: `외측 r ${col.r1}, 마디 ${col.p1Pieces.join(' / ')} (첫 마디에 갈고리 ${col.p1Hook}, 기둥철근.md §3.2)` });
|
||||
rows.push({ member: 'column', name: 'P2', size: 'S32', count: col.mainP2, length: null, note: `내측 r ${col.r2}, 마디 ${col.p2Pieces.join(' / ')}` });
|
||||
rows.push({ member: 'column', name: '커플러', size: 'L150 Ø60', count: col.couplers, length: 150, note: '@3000, P2 +150' });
|
||||
rows.push({ member: 'column', name: 'P3', size: 'H19', count: col.p3Count, length: col.p3Length, note: `일반 구간 띠철근 @300, 겹침 ${col.lapP3} (추천안, Q-C6)` });
|
||||
rows.push({ member: 'column', name: 'P5', size: 'H19', count: col.p5Count, length: col.p5Length, note: '단부 띠철근 @120, 커플러 1개' });
|
||||
@@ -182,7 +202,9 @@ export function rebarSummary(model: PierModel, m: Material = DEFAULT_MATERIAL):
|
||||
// the length is the longest of them (the largest footing governs the bar).
|
||||
const agg = new Map<string, FootingBar>();
|
||||
for (const f of model.footings) {
|
||||
for (const b of footingBarList(f.length * 1000, f.width * 1000, (f.top - f.bottom) * 1000)) {
|
||||
// Columns on this footing, as x mm from the footing centre (for the B9 column-zone rule).
|
||||
const columnX = model.columns.filter((c) => Math.abs(c.s - f.s) <= f.length / 2).map((c) => (c.s - f.s) * 1000);
|
||||
for (const b of footingBarList(f.length * 1000, f.width * 1000, (f.top - f.bottom) * 1000, columnX)) {
|
||||
const cur = agg.get(b.name);
|
||||
if (cur) {
|
||||
cur.count += b.count;
|
||||
|
||||
Reference in new issue
Block a user