Files
Pier_v1/src/core/rebar/coping.ts
T
ypshimandClaude Haiku 5.5 1074fdf428 feat(view): shear-segment table for 2·3주형 on the ③ coping tab
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>
2026-10-22 12:22:04 +09:00

182 lines
8.6 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.
/**
* 코핑 철근 (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 };
}