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
+148
-4
@@ -2,6 +2,7 @@
|
||||
* 코핑 철근 (plan C3). Source: RAG/코핑철근.md, 2026-10-22 최종 정리 (spec §6.4 is older and is not used here).
|
||||
* Positions are along the coping length from its start, in mm.
|
||||
*/
|
||||
import { DEFAULT_MATERIAL, developmentLength, hookLength, Material, NOMINAL_DIAMETER } from './common';
|
||||
export const COPING_REBAR = {
|
||||
/** 피복 (최외측 철근 중심까지). */
|
||||
cover: 100,
|
||||
@@ -15,6 +16,8 @@ export const COPING_REBAR = {
|
||||
spacingS: 250,
|
||||
/** C12·C13 측면 수평 간격. */
|
||||
spacingSide: 250,
|
||||
/** 테이퍼 높이 h2 (코핑철근.md §2). */
|
||||
taperHeight: 1500,
|
||||
/** C14 위 2줄 간격 / 아래 줄 수평 간격 / 아래 줄 연직 간격 / 끝 여유. */
|
||||
c14: { upperSpacing: 1000, lowerSpacing: 2000, verticalSpacing: 500, endGap: 500 },
|
||||
} as const;
|
||||
@@ -39,7 +42,7 @@ export interface ShearBars {
|
||||
*/
|
||||
export function shearBars(coping: number, xc = coping / 2): ShearBars {
|
||||
const { spacingL, spacingS, levelWidth } = COPING_REBAR;
|
||||
const kMax = Math.floor(levelWidth / 2 / spacingL) + 1;
|
||||
const kMax = Math.ceil(levelWidth / 2 / spacingL); // k_max = ceil((W_L/2)/s_L), spec §3.1
|
||||
const c10 = Array.from({ length: 2 * kMax + 1 }, (_, i) => Math.round(xc + (i - kMax) * spacingL));
|
||||
const left = c10[0];
|
||||
const right = c10[c10.length - 1];
|
||||
@@ -90,12 +93,153 @@ export function shearULength(x: number, coping: number): number {
|
||||
return SHEAR_U.flat + 2 * shearUHeight(x, coping);
|
||||
}
|
||||
|
||||
/** C12 straight lengths per row (코핑철근.md §6 table, mm) and C13. */
|
||||
export const SIDE_LENGTHS = { C12: [3717, 5333, 6950, 8567, 10183], C13: 11746 } as const;
|
||||
/** C13 sits 127 in from each end face (코핑철근.md §6.2). */
|
||||
export const SIDE_C13_END = 127;
|
||||
|
||||
/** Leg of each ㄷ형 bar along the side, 1.3·l_d = 1525 (Q-K9). */
|
||||
export const SIDE_HOOK_LEG = 1525;
|
||||
|
||||
export interface SideBar {
|
||||
/** Row height (m, EL). */
|
||||
z: number;
|
||||
/** Distance from each end face to where the 일자 bar starts (mm). */
|
||||
xw: number;
|
||||
/** Length of the 일자 bar, L − 2·x_w (mm). */
|
||||
straight: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* C12 and C13 rows for a coping of length L (mm), T형 (코핑철근.md §6.2).
|
||||
* C12 meets the sloped bottom at x_w = c + (z_break − z_k)·(x_L0 − c)/h2, with x_L0 = L/2 − W_L/2.
|
||||
* C13 starts at x_w = 127. Each row has two 일자 bars (side faces) and two ㄷ bars (one at each end).
|
||||
*/
|
||||
export function copingSideBars(L: number, zBottom: number, zBreak: number): { c12: SideBar[]; c13: SideBar[] } {
|
||||
const c = COPING_REBAR.cover;
|
||||
const xL0 = L / 2 - COPING_REBAR.levelWidth / 2;
|
||||
const bar = (z: number, xw: number): SideBar => ({ z, xw, straight: L - 2 * xw });
|
||||
const rows = sideRows(zBottom, zBreak);
|
||||
return {
|
||||
c12: rows.c12.map((z) => bar(z, c + ((zBreak - z) * 1000 * (xL0 - c)) / COPING_REBAR.taperHeight)),
|
||||
c13: rows.c13.map((z) => bar(z, SIDE_C13_END)),
|
||||
};
|
||||
}
|
||||
|
||||
/** C15 (거꾸로 된 U, 3852) and C16 (L형, 4070 / 4069) lengths, mm (코핑철근.md §7). */
|
||||
export const STEP_LENGTHS = { C15: 3852, C16: [4070, 4069] as const, C14: 3447.5 } as const;
|
||||
|
||||
/**
|
||||
* Stair zone of the P1 coping (코핑철근.md §7.1): each segment from x to x (mm from the coping start), and its top rise
|
||||
* above the lowest top (mm). The stair positions are the document's; the model has no stair geometry yet.
|
||||
*/
|
||||
export const STAIR_SEGMENTS = [
|
||||
{ from: 2196, to: 4701, rise: 72 }, // 2구간
|
||||
{ from: 4701, to: 7207, rise: 144 }, // 3구간
|
||||
{ from: 7207, to: 9712, rise: 215 }, // 4구간
|
||||
{ from: 9712, to: 12000, rise: 287 }, // 5구간 (to = L in the document)
|
||||
] as const;
|
||||
|
||||
/** C14 end gap e_14 (코핑철근.md §8.1) and the inset of its ends from the side faces: c + (d_shear + d_14)/2 = 122.25. */
|
||||
const C14_END_GAP = 500;
|
||||
export const C14_INSET = COPING_REBAR.cover + (NOMINAL_DIAMETER.H25 + NOMINAL_DIAMETER.H19) / 2;
|
||||
|
||||
/** Development length l_d of H19 (882 for fy 400, fck 27), rounded to mm as the document does (§7.2). */
|
||||
export function stepDevLength(m: Material = DEFAULT_MATERIAL): number {
|
||||
return Math.round(developmentLength(NOMINAL_DIAMETER.H19, m.fyH, m.fck));
|
||||
}
|
||||
|
||||
export interface C14Bar {
|
||||
/** 1–2: upper lines (C2, C3). 3–6: lower rows from the 7th, 5th, 3rd and 1st side rows. */
|
||||
line: number;
|
||||
/** Position along the coping (mm from its start). */
|
||||
x: number;
|
||||
/** EL of the bar (m). */
|
||||
z: number;
|
||||
/** false when the 연직 발 would end below the coping underside at x, so both legs are left out (§8). */
|
||||
legs: boolean;
|
||||
}
|
||||
|
||||
/** Underside of the coping (EL m) at x (mm from the start), T형: sloped from each end to the Level zone, flat in the middle. */
|
||||
export function undersideAt(L: number, x: number, endBottom: number, levelBottom: number): number {
|
||||
const xL0 = L / 2 - COPING_REBAR.levelWidth / 2;
|
||||
return endBottom + (levelBottom - endBottom) * Math.min(Math.min(x, L - x) / xL0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* C14 ⊓ bars across the width (코핑철근.md §8.1). Upper lines hang from C2 and C3 at @1000 from the column centre.
|
||||
* Lower rows use the side rows 7, 5, 3, 1 from the bottom (k = m − 2(j − 1)): odd j at x_c ± 2000k, even j at x_c ± 1000 ± 2000k,
|
||||
* only where that side row exists (x_w ≤ x ≤ L − x_w). `sideRows` are the side rows (z m, x_w mm), ascending.
|
||||
*/
|
||||
export function c14Bars(L: number, topEl: number, sideRows: { z: number; xw: number }[], under: (x: number) => number): C14Bar[] {
|
||||
const leg = Math.round(hookLength('main90', NOMINAL_DIAMETER.H19)) / 1000; // 296 mm, EL m
|
||||
const xc = L / 2;
|
||||
const lo = COPING_REBAR.cover + C14_END_GAP;
|
||||
const hi = L - lo;
|
||||
const out: C14Bar[] = [];
|
||||
const dUpper = (NOMINAL_DIAMETER.H29 + NOMINAL_DIAMETER.H19) / 2000; // m, over C2 / C3 (H29)
|
||||
for (const k of [2, 3]) {
|
||||
const z = topEl - 0.127 - (k - 1) * 0.057 + dUpper;
|
||||
for (let q = -10; q <= 10; q++) {
|
||||
const x = xc + q * 1000;
|
||||
if (x >= lo && x <= hi) out.push({ line: k - 1, x, z, legs: z - leg >= under(x) });
|
||||
}
|
||||
}
|
||||
const dLower = (NOMINAL_DIAMETER.H25 + NOMINAL_DIAMETER.H19) / 2000; // m, over a H25 side row
|
||||
for (let j = 1; 7 - 2 * (j - 1) >= 1; j++) {
|
||||
const row = sideRows[7 - 2 * (j - 1) - 1];
|
||||
const a = Math.max(lo, row.xw);
|
||||
const b = Math.min(hi, L - row.xw);
|
||||
for (let q = -10; q <= 10; q++) {
|
||||
const x = xc + (j % 2 === 1 ? 0 : 1000) + 2000 * q;
|
||||
if (x >= a && x <= b) out.push({ line: 2 + j, x, z: row.z + dLower, legs: row.z + dLower - leg >= under(x) });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** C14 length (mm): the straight run between the inset ends plus two 15.5·d_b legs (§8.2). 3447.5 in the document. */
|
||||
export function c14Length(B: number): number {
|
||||
return B - 2 * C14_INSET + 2 * Math.round(hookLength('main90', NOMINAL_DIAMETER.H19)); // legs rounded to mm (296)
|
||||
}
|
||||
|
||||
export interface StepBar {
|
||||
/** Position across the width, mm from the centre line. */
|
||||
n: number;
|
||||
/** Start and end along the coping (mm). */
|
||||
x0: number;
|
||||
x1: number;
|
||||
/** EL of the horizontal run (m). */
|
||||
z: number;
|
||||
/** Legs: C15 hangs at both ends, C16 only at its start. */
|
||||
legs: 'both' | 'start';
|
||||
/** Leg length l_d (mm). */
|
||||
leg: number;
|
||||
/** Developed length (mm): the horizontal run plus the leg(s) at its ends. */
|
||||
length: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* C15 (거꾸로 된 U, on the highest segment) and C16 (L형, on segments 3 and 4), 11 bars each across the width (코핑철근.md §7).
|
||||
* Across: 137 from the cover line, then @250 (피복선 기준). Along: from each segment start + c to the next end − c.
|
||||
*/
|
||||
export function stairBars(L: number, B: number, topEl: number, m: Material = DEFAULT_MATERIAL): { c15: StepBar[]; c16: StepBar[] } {
|
||||
const ld = stepDevLength(m);
|
||||
const c = COPING_REBAR.cover;
|
||||
const ns = Array.from({ length: 11 }, (_, k) => -B / 2 + c + 137 + 250 * k);
|
||||
const seg = STAIR_SEGMENTS;
|
||||
const top = (i: number) => topEl + seg[i].rise / 1000;
|
||||
const c15Z = top(3) - c / 1000;
|
||||
const c16Z = (i: number) => top(i) - c / 1000 - NOMINAL_DIAMETER.H19 / 1000; // (d_C15 + d_C16)/2 = 19.1
|
||||
const x0c15 = seg[3].from + c;
|
||||
const x1c15 = L - c;
|
||||
const c15: StepBar[] = ns.map((n) => ({ n, x0: x0c15, x1: x1c15, z: c15Z, legs: 'both', leg: ld, length: x1c15 - x0c15 + 2 * ld }));
|
||||
const c16: StepBar[] = [1, 2].flatMap((i) => {
|
||||
const x0 = seg[i].from + c;
|
||||
const x1 = seg[i].to - c + ld; // the horizontal run runs on past the segment end by l_d
|
||||
return ns.map((n): StepBar => ({ n, x0, x1, z: c16Z(i), legs: 'start', leg: ld, length: x1 - x0 + ld }));
|
||||
});
|
||||
return { c15, c16 };
|
||||
}
|
||||
|
||||
/**
|
||||
* 삿갓 (upper cap) of C9–C11 (코핑철근.md §4.3). l_cap is open (Q-K5); recommended here: l_cap = h − c (top cover 100),
|
||||
* so that l_cap stays below h (RB-K04). Cap length = 1740 + 2·l_cap.
|
||||
@@ -152,7 +296,7 @@ export function shearBarsMulti(coping: number, columns: number[]): ShearBars & {
|
||||
return { ...one, between: [] };
|
||||
}
|
||||
const { spacingL, spacingS, levelWidth } = COPING_REBAR;
|
||||
const kMax = Math.floor(levelWidth / 2 / spacingL) + 1;
|
||||
const kMax = Math.ceil(levelWidth / 2 / spacingL); // k_max = ceil((W_L/2)/s_L), spec §3.1
|
||||
const groups = columns.map((xc) =>
|
||||
Array.from({ length: 2 * kMax + 1 }, (_, i) => Math.round(xc + (i - kMax) * spacingL)),
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user