T형은 P1 문서의 고정 위치(2.196~12.000 m)·높이·11개를 써서 코핑 길이가 다르면 철근이 코핑 밖으로 나갔고, 다주형은 높은 계단 전체에 C15 하나만 두었다. 공통 규칙 (step-bars.ts): - 열마다(전·후열 단차가 있으면 각 쪽) 최저 상면 + h_16(100) 이상 구간이 계단, 경계는 mm 반올림 - 이웃보다 모두 높은 구간 C15(⊓), 나머지 계단 C16(L형, 낮은 쪽 다리, 높은 쪽으로 l_d 연장) - 폭 방향: T형은 주철근 내부 격자 하나 건너, 다주형은 상면 주철근 하나 건너 - 양쪽이 높은 계단은 RB-S01 알림, RB-M04 폐지. 꺾임은 편 좌표의 한 개(꺾은 철근) P1 문서값(C15 11 × 3852, C16 22 × 4070/4069, 137.5 · 10@250)을 그대로 재현한다. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
81 lines
3.8 KiB
TypeScript
81 lines
3.8 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { buildPier } from '../src/core/geometry';
|
||
import { c14Bars, c14Length, copingSideBars, tStepBars, undersideAt } from '../src/core/rebar/coping';
|
||
import { loadP1 } from './helpers';
|
||
|
||
const m = buildPier(loadP1());
|
||
const L = m.copingL * 1000;
|
||
const B = m.copingB * 1000;
|
||
const side = copingSideBars(L, 41.288, 42.538);
|
||
const rows = [...side.c13, ...side.c12];
|
||
const under = (x: number) => undersideAt(L, x, m.endBottom, m.levelBottom);
|
||
|
||
describe('C14 / C15 / C16 (코핑철근.md §7–8)', () => {
|
||
it('C14: 2 upper lines of 11 (C2, C3) and lower rows 5 / 6 / 3 / 2 = 38', () => {
|
||
const bars = c14Bars(L, m.copingRefTop, rows, under);
|
||
const count = (line: number) => bars.filter((b) => b.line === line).length;
|
||
expect(count(1)).toBe(11);
|
||
expect(count(2)).toBe(11);
|
||
expect([3, 4, 5, 6].map(count)).toEqual([5, 6, 3, 2]);
|
||
expect(bars.length).toBe(38);
|
||
});
|
||
|
||
it('C14 heights: upper 43.208 / 43.108, lower 42.810 / 42.310 / 41.810 / 41.310 (상면 단 간격 100)', () => {
|
||
const bars = c14Bars(L, m.copingRefTop, rows, under);
|
||
const z = (line: number) => bars.find((b) => b.line === line)!.z;
|
||
expect(z(1)).toBeCloseTo(43.208, 3);
|
||
expect(z(2)).toBeCloseTo(43.108, 3);
|
||
expect([3, 4, 5, 6].map(z)).toEqual([42.81, 42.31, 41.81, 41.31].map((v) => expect.closeTo(v, 2)));
|
||
});
|
||
|
||
it('C14 length 3447.5 (2855.5 run + 2 × 296 legs)', () => {
|
||
expect(c14Length(B)).toBeCloseTo(3447.5, 0);
|
||
});
|
||
|
||
// P1 문서값 (§7) = 일반 규칙의 기준 (결정 2026-11-10): 모델의 계단 72/144/215/287 → C15 5구간, C16 3·4구간.
|
||
const steps = tStepBars(m).bars;
|
||
const c15 = steps.filter((b) => b.name === 'C15');
|
||
const c16 = steps.filter((b) => b.name === 'C16');
|
||
|
||
it('C15: 11 bars, z 43.598, length 3852 (2088 + 2 × 882), from 9.812 to 11.900', () => {
|
||
expect(c15.length).toBe(11);
|
||
expect(c15[0].z).toBeCloseTo(43.598, 3);
|
||
expect(c15[0].length).toBe(3852);
|
||
expect([c15[0].x0, c15[0].x1]).toEqual([9812, 11900]);
|
||
});
|
||
|
||
it('C16: 22 bars (3·4구간), z 43.436 / 43.507, lengths 4070 / 4069, leg at the low side', () => {
|
||
expect(c16.length).toBe(22);
|
||
expect(c16[0].z).toBeCloseTo(43.436, 3);
|
||
expect(c16[11].z).toBeCloseTo(43.507, 3);
|
||
expect(c16[0].length).toBe(4070);
|
||
expect(c16[11].length).toBe(4069);
|
||
expect([c16[0].x0, c16[0].x1, c16[11].x0, c16[11].x1]).toEqual([4801, 7989, 7307, 10494]);
|
||
expect(c16.every((b) => b.legs === 'start')).toBe(true);
|
||
});
|
||
|
||
it('C15 / C16 across positions: 137.5 from the cover line (문서 137), then @250, 11 bars', () => {
|
||
expect(c15[0].n * 1000).toBeCloseTo(-B / 2 + 237.5, 6);
|
||
expect((c15[10].n - c15[0].n) * 1000).toBeCloseTo(2500, 6);
|
||
});
|
||
});
|
||
|
||
describe('C14 연직 발 (코핑철근.md §8)', () => {
|
||
it('upper lines keep their legs; a lower bar keeps its legs only if they end above the underside', () => {
|
||
const bars = c14Bars(L, m.copingRefTop, rows, under);
|
||
expect(bars.filter((b) => b.line <= 2).every((b) => b.legs)).toBe(true);
|
||
// The leg of the C12 5단 bar (line 4) at the ends (x 1 m, 11 m) would end below the sloped underside, so it is left out.
|
||
const omitted = bars.filter((b) => !b.legs).map((b) => [b.line, Math.round(b.x / 1000)]);
|
||
expect(omitted).toEqual([[4, 1], [4, 11]]);
|
||
});
|
||
});
|
||
|
||
describe('C15 간격 = 2 × 주철근 기본간격 (승인 2026-10-26)', () => {
|
||
it('the default pitch is 250, and a 140 기본간격 moves the C15 bars to 280', () => {
|
||
const base = tStepBars(m).bars.filter((b) => b.name === 'C15');
|
||
expect(Math.round((base[1].n - base[0].n) * 1000)).toBe(250);
|
||
const wider = tStepBars(buildPier({ ...loadP1(), copingRebar: { mainSpacing: 140 } })).bars.filter((b) => b.name === 'C15');
|
||
expect(Math.round((wider[1].n - wider[0].n) * 1000)).toBe(280);
|
||
});
|
||
});
|