feat(rebar): 꺾인 코핑 철근을 꺾임에 맞춰 접는다 (교축 꺾은 철근 · 미터 전단 1조)
편 좌표(g)로 계산한 다주형 코핑 철근을 꺾인 코핑에 맞춘다 (coping-fold.ts): - 교축 방향 철근은 그 n에서 미터면과 만나는 점에서 꺾은 한 개, 길이는 실제 경로 (바깥 길고 안쪽 짧음) - 교축 직각 철근(전단·크로스타이)은 미터 쐐기에 걸리면 빼고, 꺾임점에 가장 가까운 전단 위치 1조를 미터 방향으로 - 상면 1단 이음은 꺾임점에서 l_s/2 + 미터 퍼짐 밖으로 조각(pieces)은 구간별 국부 좌표로 담아 3D가 그대로 그릴 수 있게 한다. 곧은 코핑은 바뀌지 않는다. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
51209cccf9
commit
6a15a13fc7
3 files changed
+145
-2
No files matched your search
@@ -7,6 +7,7 @@ import { buildPier, DIM, defaultLevelWidthMm } from '../src/core/geometry';
|
||||
import type { PierInput } from '../src/core/types';
|
||||
import { rebarSummary } from '../src/core/rebar/summary';
|
||||
import { multicolCopingInfo } from '../src/core/rebar/multicol-coping';
|
||||
import { copingFold, inWedge, isTransverse } from '../src/core/rebar/coping-fold';
|
||||
|
||||
// 산외교 P11 (샘플01) with the coping bent 8.767° at IP4 (2026-11-10 검토): back ⑥ g 11.64, ⑦ g 18.49, kink g 15.14.
|
||||
const SAMPLE = fileURLToPath(new URL('../project_data/01_엑셀/샘플01_산외교_교각입력데이터.xlsx', import.meta.url));
|
||||
@@ -145,3 +146,43 @@ describe('꺾인 다주형 코핑의 철근표 (A, 2026-11-10)', () => {
|
||||
expect(rows.every((r) => r.note.includes('다주형'))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('꺾인 코핑 철근 접기 (B·C, 2026-11-10)', () => {
|
||||
const m = buildPier(p11());
|
||||
const f = copingFold(m)!;
|
||||
const bars = multicolCopingInfo(m).bars;
|
||||
const unfolded = (pts: [number, number, number][]) => pts.slice(1).reduce((s, p, i) => s + Math.hypot(p[0] - pts[i][0], p[1] - pts[i][1], p[2] - pts[i][2]), 0) * 1000;
|
||||
|
||||
it('a run across the kink turns at its mitre point: length = unfolded + a0(n) − a1(n), outer longer, inner shorter', () => {
|
||||
const crossing = bars.filter((b) => b.name === 'C7' && b.pieces!.length === 2);
|
||||
expect(crossing.length).toBeGreaterThan(0);
|
||||
for (const b of crossing) {
|
||||
const n = b.pts[1][2];
|
||||
expect(b.length).toBeCloseTo(unfolded(b.pts) + (f.a0(n) - f.a1(n)) * 1000, 6);
|
||||
}
|
||||
const byN = [...crossing].sort((a, b) => a.pts[1][2] - b.pts[1][2]);
|
||||
const d = (b: (typeof crossing)[number]) => b.length - unfolded(b.pts);
|
||||
expect(Math.sign(d(byN[0]))).toBe(-Math.sign(d(byN.at(-1)!)));
|
||||
expect(Math.abs(d(byN[0]))).toBeGreaterThan(100);
|
||||
});
|
||||
|
||||
it('no bar across the width cuts the mitre, except the one shear set laid on it', () => {
|
||||
const transverse = bars.filter((b) => isTransverse(b.pts));
|
||||
const onIt = transverse.filter((b) => b.pieces!.length === 1 && b.pieces![0].pts.every((p) => Math.abs(p[0] - (f.kinkG + f.a0(p[2]))) < 0.2));
|
||||
expect(new Set(onIt.map((b) => b.name)).size).toBeGreaterThan(0);
|
||||
for (const b of transverse.filter((x) => !onIt.includes(x))) expect(inWedge(f, b.pts)).toBe(false);
|
||||
});
|
||||
|
||||
it('the top-bar laps keep l_s/2 + the wedge away from the kink', () => {
|
||||
const c1 = bars.filter((b) => b.name === 'C1');
|
||||
const ends = c1.flatMap((b) => [Math.min(...b.pts.map((p) => p[0])), Math.max(...b.pts.map((p) => p[0]))]);
|
||||
const inner = ends.filter((x) => x > 0.2 && x < m.copingL - 0.2);
|
||||
expect(inner.length).toBeGreaterThan(0);
|
||||
for (const x of inner) expect(Math.abs(x - f.kinkG)).toBeGreaterThan(f.wedge - 1e-9);
|
||||
});
|
||||
|
||||
it('a straight coping is unchanged (no pieces)', () => {
|
||||
const straight = buildPier({ ...p11(), ips: p11().ips.map((q) => (q.name === 'IP4' ? { ...q, angleDeg: 0 } : q)) });
|
||||
expect(multicolCopingInfo(straight).bars.every((b) => b.pieces === undefined)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user