The segment table lists each column's Level and the segments between columns (추천안, Q-K2), so a 2주형 shows 좌 경사 3, Level 1 9, Level 2 9, 기둥 사이 1 18, 우 경사 3. One column is unchanged. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
182 lines
8.6 KiB
TypeScript
182 lines
8.6 KiB
TypeScript
/**
|
||
* 코핑 철근 (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.
|
||
*/
|
||
export const COPING_REBAR = {
|
||
/** 피복 (최외측 철근 중심까지). */
|
||
cover: 100,
|
||
/** 첫 전단철근 최소 여유 e_add. 끝 여유 e_min = cover + e_add = 300. */
|
||
eAdd: 200,
|
||
/** Level 폭 W_L. */
|
||
levelWidth: 2100,
|
||
/** C10 간격 s_L (기둥 중심부터). */
|
||
spacingL: 300,
|
||
/** C9·C11 간격 s_s (C10에서 바깥으로). */
|
||
spacingS: 250,
|
||
/** C12·C13 측면 수평 간격. */
|
||
spacingSide: 250,
|
||
/** C14 위 2줄 간격 / 아래 줄 수평 간격 / 아래 줄 연직 간격 / 끝 여유. */
|
||
c14: { upperSpacing: 1000, lowerSpacing: 2000, verticalSpacing: 500, endGap: 500 },
|
||
} as const;
|
||
|
||
export const COPING_END_GAP = COPING_REBAR.cover + COPING_REBAR.eAdd;
|
||
|
||
export interface ShearBars {
|
||
/** C10 positions (mm), from the first to the last. */
|
||
c10: number[];
|
||
/** C9 positions (mm), from the left end toward the column. */
|
||
c9: number[];
|
||
/** C11 positions (mm), from the column toward the right end. */
|
||
c11: number[];
|
||
/** Distance from the coping end to the outermost C9 / C11 bar (mm). */
|
||
endGap: number;
|
||
}
|
||
|
||
/**
|
||
* Shear bars C9–C11 along the axis (코핑철근.md §3). A single column at the coping centre.
|
||
* C10: @300 from the column centre, out to the first position beyond the Level edge (k_max).
|
||
* C9 / C11: @250 outward from the outermost C10, as many as fit with the end gap of at least 300.
|
||
*/
|
||
export function shearBars(coping: number, xc = coping / 2): ShearBars {
|
||
const { spacingL, spacingS, levelWidth } = COPING_REBAR;
|
||
const kMax = Math.floor(levelWidth / 2 / spacingL) + 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];
|
||
const n9 = Math.floor((left - COPING_END_GAP) / spacingS);
|
||
if (n9 < 0) {
|
||
throw new Error('RB-K01: C10 바깥 구간이 끝 여유보다 짧아 C9를 놓을 수 없습니다.');
|
||
}
|
||
const c9 = Array.from({ length: n9 }, (_, j) => Math.round(left - (n9 - j) * spacingS));
|
||
const c11 = Array.from({ length: n9 }, (_, j) => Math.round(right + (j + 1) * spacingS));
|
||
const endGap = Math.min(left - c9[0], coping - c11[c11.length - 1]);
|
||
return { c10, c9, c11, endGap: endGap };
|
||
}
|
||
|
||
/**
|
||
* Side horizontal bars (코핑철근.md §6): seven rows from the bottom at 250 mm.
|
||
* Rows below the sloped-bottom break (z_7,end) are C12, the rows at and above it are C13.
|
||
*/
|
||
export function sideRows(zBottom: number, zBreak: number, rows = 7): { c12: number[]; c13: number[] } {
|
||
const z = Array.from({ length: rows }, (_, i) => Math.round((zBottom + i * COPING_REBAR.spacingSide / 1000) * 1000) / 1000);
|
||
return { c12: z.filter((v) => v < zBreak - 1e-9), c13: z.filter((v) => v >= zBreak - 1e-9) };
|
||
}
|
||
|
||
/** C14 counts (코핑철근.md §8): 2 upper lines of 11 plus the lower rows 5 / 6 / 3 / 2 = 38. */
|
||
export const C14_COUNTS = { upperLines: 2, upperPerLine: 11, lower: [5, 6, 3, 2] as const };
|
||
|
||
export function c14Total(): number {
|
||
return C14_COUNTS.upperLines * C14_COUNTS.upperPerLine + C14_COUNTS.lower.reduce((a, b) => a + b, 0);
|
||
}
|
||
|
||
/** C15 (거꾸로 된 U, 가장 높은 계단) and C16 (L형, 2구간) counts (코핑철근.md §7). */
|
||
export const STEP_BAR_COUNTS = { C15: 11, C16: 22 } as const;
|
||
|
||
/**
|
||
* C9·C10 lower U length (코핑철근.md §4.3): 1740 + 2h. h rises linearly along the slope between the document's
|
||
* ends (x 300 → h 890.9, x 4550 → h 2178.8) and stays at the Level height 2300 from the Level edge (4950) on.
|
||
* The bar's upper cap (삿갓) length depends on l_cap, which is still open (Q-K5), so only the U is given here.
|
||
*/
|
||
export const SHEAR_U = { x0: 300, h0: 890.9, x1: 4550, h1: 2178.8, levelH: 2300, levelEdgeFromCentre: 1050, flat: 1740 } as const;
|
||
|
||
export function shearUHeight(x: number, coping: number): number {
|
||
const xs = Math.min(x, coping - x); // mirror to the left half
|
||
const slope = (SHEAR_U.h1 - SHEAR_U.h0) / (SHEAR_U.x1 - SHEAR_U.x0);
|
||
const h = SHEAR_U.h0 + (xs - SHEAR_U.x0) * slope;
|
||
return Math.min(h, SHEAR_U.levelH);
|
||
}
|
||
|
||
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;
|
||
|
||
/** 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;
|
||
|
||
/**
|
||
* 삿갓 (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.
|
||
*/
|
||
export const CAP = { flat: 1740, cover: 100 } as const;
|
||
|
||
export function capLength(x: number, coping: number): number {
|
||
const h = shearUHeight(x, coping);
|
||
const lCap = h - CAP.cover;
|
||
return CAP.flat + 2 * lCap;
|
||
}
|
||
|
||
/** Pieces per position: 3 closed stirrups × (U + cap) = 6. Total 270 for C9 + C10 + C11 (document). */
|
||
export const PIECES_PER_POSITION = 6;
|
||
|
||
/**
|
||
* 전단철근 구간 표 (시안 ③ · 코핑): the segments of C9 / C10 / C11 along the coping, from the bar positions.
|
||
* One column gives the three T형 segments; several columns add the segments between them (추천안, Q-K2).
|
||
*/
|
||
export function shearSegments(coping: number, columns: number[] = [coping / 2]): { segment: string; from: number; to: number; spacing: number; count: number; bar: 'C9' | 'C10' | 'C11' | 'C9·C11' }[] {
|
||
const b = shearBarsMulti(coping, columns);
|
||
const seg = (name: string, xs: number[], bar: 'C9' | 'C10' | 'C11' | 'C9·C11', spacing: number) => ({
|
||
segment: name,
|
||
from: Math.min(...xs),
|
||
to: Math.max(...xs),
|
||
spacing,
|
||
count: xs.length,
|
||
bar,
|
||
});
|
||
const out = [seg('좌 경사', b.c9, 'C9', COPING_REBAR.spacingS)];
|
||
const groups = columns.length > 1 ? columns.length : 1;
|
||
for (let k = 0; k < groups; k++) {
|
||
const xc = columns[k] ?? coping / 2;
|
||
out.push(seg(`Level ${k + 1}`, b.c10.filter((x) => Math.abs(x - xc) <= (COPING_REBAR.levelWidth / 2 + COPING_REBAR.spacingL * 4)).slice(0, 2 * 4 + 1), 'C10', COPING_REBAR.spacingL));
|
||
}
|
||
for (let k = 0; k + 1 < groups; k++) {
|
||
const between = b.between.filter((x) => x > columns[k] && x < columns[k + 1]);
|
||
if (between.length) out.push(seg(`기둥 사이 ${k + 1}`, between, 'C9·C11', COPING_REBAR.spacingS));
|
||
}
|
||
out.push(seg('우 경사', b.c11, 'C11', COPING_REBAR.spacingS));
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* Shear bars for a multi-column coping (코핑철근.md §9, 다주형 확장 원칙).
|
||
* C10: each column's centre, @300 out to the first position beyond its Level edge.
|
||
* C9 / C11: outward from the outermost C10 on each end, as for one column.
|
||
* Between columns: @250 in from both C10 groups, the remaining length shared evenly (추천안, Q-K2).
|
||
* With one column this is the same as shearBars.
|
||
*/
|
||
export function shearBarsMulti(coping: number, columns: number[]): ShearBars & { between: number[] } {
|
||
if (columns.length <= 1) {
|
||
const one = shearBars(coping, columns[0] ?? coping / 2);
|
||
return { ...one, between: [] };
|
||
}
|
||
const { spacingL, spacingS, levelWidth } = COPING_REBAR;
|
||
const kMax = Math.floor(levelWidth / 2 / spacingL) + 1;
|
||
const groups = columns.map((xc) =>
|
||
Array.from({ length: 2 * kMax + 1 }, (_, i) => Math.round(xc + (i - kMax) * spacingL)),
|
||
);
|
||
for (let i = 1; i < groups.length; i++) {
|
||
if (groups[i][0] <= groups[i - 1][groups[i - 1].length - 1]) {
|
||
throw new Error('기둥 간격이 좁아 C10 구간이 겹칩니다. 다주형 코핑 철근을 만들 수 없습니다.');
|
||
}
|
||
}
|
||
const c10 = groups.flat();
|
||
const left = groups[0][0];
|
||
const right = groups[groups.length - 1][groups[groups.length - 1].length - 1];
|
||
const n9 = Math.floor((left - COPING_END_GAP) / spacingS);
|
||
if (n9 < 0) throw new Error('RB-K01: C10 바깥 구간이 끝 여유보다 짧아 C9를 놓을 수 없습니다.');
|
||
const c9 = Array.from({ length: n9 }, (_, j) => Math.round(left - (n9 - j) * spacingS));
|
||
const c11 = Array.from({ length: n9 }, (_, j) => Math.round(right + (j + 1) * spacingS));
|
||
const between: number[] = [];
|
||
for (let i = 1; i < groups.length; i++) {
|
||
const a = groups[i - 1][groups[i - 1].length - 1];
|
||
const b = groups[i][0];
|
||
const gap = b - a;
|
||
const pitches = Math.floor(gap / spacingS);
|
||
for (let j = 1; j < pitches; j++) between.push(Math.round(a + (gap * j) / pitches));
|
||
}
|
||
const endGap = Math.min(left - c9[0], coping - c11[c11.length - 1]);
|
||
return { c10, c9, c11, between, endGap };
|
||
}
|