feat(abut-rebar): ③ 교대 흉벽 · 브라켓 3D 철근 (P1–P11)

- 흉벽 배근 경로를 교대 3D 철근에 추가 (저판 · 벽체와 같은 장면, 선택 · 색 기준 공통)
- 테스트: 철근표 = 3D 개수, 모든 흉벽 철근이 브라켓 배면 ~ 흉벽 전면, 흉벽 상단 아래 (P10은 슬래브로 300)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-04 17:06:28 +09:00
1 parent 9f152fef90
commit 51a36669a8
3 files changed
+52 -9

No files matched your search

+33
View File
@@ -11,6 +11,7 @@ import { skewAngle } from '../src/core/abut/rebar/skew-layout';
import { abutSkewK } from '../src/view/abut-scene';
import { buildAbutRebar3D } from '../src/view/abut-rebar3d';
import { abutWallGeom, abutWallRebar } from '../src/core/abut/rebar/wall';
import { abutParapetGeom, abutParapetRebar } from '../src/core/abut/rebar/parapet';
// 예제 A1 (교대확대기초철근.md §13).
const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url));
@@ -98,3 +99,35 @@ describe('교대 벽체 3D 철근 (③, 2026-11-04)', () => {
}
});
});
describe('교대 흉벽 · 브라켓 3D 철근 (③, 2026-11-04)', () => {
const footing = abutFootingRebar(geom);
const wGeom = abutWallGeom(model.length, members, skewAngle(rec.skewDeg));
const wall = abutWallRebar(wGeom, footing);
const pGeom = abutParapetGeom(wGeom, members.shape, { B: 11254, a: 300, b: 11554, nearT: -505, chamferA: 300, chamferB: 300 });
const para = abutParapetRebar(pGeom, wall);
const scene = buildAbutRebar3D(geom, footing, k, back, bottom, wall, para);
it('draws every 흉벽 bar of the 철근표 (one tube per straight piece), next to the 저판 · 벽체 bars', () => {
for (const b of para.bars) {
const pieces = para.paths.filter((p) => p.mark === b.mark).reduce((a, p) => a + p.pts.length - 1, 0);
expect(scene.byName.get(b.mark)?.length, b.mark).toBe(pieces);
}
expect(scene.byName.get('W1')?.length).toBeGreaterThan(0);
expect(scene.byName.get('B1')?.length).toBeGreaterThan(0);
});
it('keeps the 흉벽 bars between the bracket back and the 흉벽 전면, below the 흉벽 상단 (P10 into the slab by 300)', () => {
const topMax = bottom + Math.max(...pGeom.top) / 1000;
const box = new THREE.Box3();
for (const b of para.bars) {
for (const mesh of scene.byName.get(b.mark)!) {
mesh.geometry.computeBoundingBox();
box.copy(mesh.geometry.boundingBox!);
expect(box.min.z, b.mark).toBeGreaterThan(wGeom.xb / 1000);
expect(box.max.z, b.mark).toBeLessThan(0);
expect(box.max.y, b.mark).toBeLessThan(topMax + (b.mark === 'P10' ? 0 : -0.05));
}
}
});
});