fix(abut): 흉벽과 벽체가 간섭하지 않도록 벽체 배면 단차를 나눔

- 벽체를 전면부(t −0.5 … 1.6, 교좌면 단차까지)와 배면 단차부(t −0.8 … −0.5, 브라켓 상면까지)로 나눔. 배면 단차 위가 접속슬래브 받침 (흉벽 상단 − 0.85)
- 흉벽은 교좌면 단차 위(t −0.5 … 0)에서 흉벽 상단까지. 벽체와 겹치지 않음
- 배면은 연직 단차 없이 같은 면(−0.8, −0.5)을 따라 올라가고, 바뀌는 곳은 수평 단차 1개
- 테스트: 벽체와 흉벽 사이 공통 체적 0, 벽체 배면 위치. 전체 636건 통과, 타입 검사·빌드 통과. 브라우저 시각 확인은 미실시

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-03 16:24:43 +09:00
1 parent 986418024a
commit 43edcdc3c5
3 files changed
+39 -14

No files matched your search

+23 -11
View File
@@ -133,25 +133,37 @@ export function buildAbutGroup(
const footingTop = members.footingTopEl;
const footingBottom = members.footingBottomEl;
// 벽체: one segment per 교좌면 step, from the footing top to the 교좌면, t −0.8 … +1.6.
// 벽체 (spec §4.3, 도면 type-1): the front part (t −0.5 … 1.6) stops at the 교좌면 step; the back ledge (t −0.8 … −0.5)
// rises to the 브라켓 상면 (흉벽 상단 − 0.85), where the 접속슬래브 sits. The two parts only touch the 흉벽 faces.
const parapetTopAt = (s: number) => members.parapetTopStartEl + ((members.parapetTopEndEl - members.parapetTopStartEl) * s) / L;
const bracketAt = (s: number) => parapetTopAt(s) - members.shape.bracketHeightM;
for (const seg of members.wallSegments) {
const profile: [number, number][] = [
const front: [number, number][] = [
[seg.sFrom, footingTop],
[seg.sTo, footingTop],
[seg.sTo, seg.seatEl],
[seg.sFrom, seg.seatEl],
];
add('wall', prism(profile, members.shape.wallFrontM - members.shape.wallBackM, new THREE.Vector3(0, 0, members.shape.wallBackM), ex, ey, ez), 0xb7c7d3);
add('wall', prism(front, members.shape.wallFrontM - members.shape.parapetBackM, new THREE.Vector3(0, 0, members.shape.parapetBackM), ex, ey, ez), 0xb7c7d3);
const ledge: [number, number][] = [
[seg.sFrom, footingTop],
[seg.sTo, footingTop],
[seg.sTo, bracketAt(seg.sTo)],
[seg.sFrom, bracketAt(seg.sFrom)],
];
add('wall', prism(ledge, members.shape.parapetBackM - members.shape.wallBackM, new THREE.Vector3(0, 0, members.shape.wallBackM), ex, ey, ez), 0xb7c7d3);
}
// 흉벽: t −0.5 … 0, from the footing top up to the parapet top line.
const parapetProfile: [number, number][] = [
[0, footingTop],
[L, footingTop],
[L, members.parapetTopEndEl],
[0, members.parapetTopStartEl],
];
add('parapet', prism(parapetProfile, -members.shape.parapetBackM, new THREE.Vector3(0, 0, members.shape.parapetBackM), ex, ey, ez), 0x8898aa);
// 흉벽: t −0.5 … 0, from the 교좌면 step (the top of the front wall) up to the parapet top line, so it never overlaps the wall.
for (const seg of members.wallSegments) {
const parapet: [number, number][] = [
[seg.sFrom, seg.seatEl],
[seg.sTo, seg.seatEl],
[seg.sTo, parapetTopAt(seg.sTo)],
[seg.sFrom, parapetTopAt(seg.sFrom)],
];
add('parapet', prism(parapet, -members.shape.parapetBackM, new THREE.Vector3(0, 0, members.shape.parapetBackM), ex, ey, ez), 0x8898aa);
}
if (footingBottom !== undefined) {
// 저판: s 0 … L, t −1.8 … +2.8, from the 기초 하단 to the 저판 상면.