fix(abut): 날개벽·헌치·접속슬래브가 벽체와 흉벽 안으로 들어가지 않도록

- 날개벽: 벽체 배면(t −0.8) 뒤로만 세움. 벽체·흉벽 앞쪽 부분은 잘라냄 (프로파일을 dM ≥ 벽체 배면 위치로 자름)
- 위 헌치: 브라켓 상면이 s 방향으로 기울므로, 헌치 면적 안에서 가장 높은 브라켓 상면 위에 놓음
- 접속슬래브: 흉벽 배면 단차 위(t ≥ 벽체 배면)는 브라켓 상면에 평평하게 얹고, 벽체 뒤쪽에서만 4 % 하향
- 테스트: 날개벽 t ≤ −0.8, 벽체 배면 위치, 위 헌치와 슬래브가 배면 단차 아래로 내려가지 않음. 전체 638건 통과, 타입 검사·빌드 통과. 브라우저 시각 확인은 미실시

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-03 16:35:48 +09:00
1 parent b425a6010b
commit 7fab9b6685
4 files changed
+54 -6

No files matched your search

+19 -2
View File
@@ -79,6 +79,21 @@ function toAxis(model: AbutModel, p: AbutPoint): { s: number; t: number } {
return { s: d.e * model.u.e + d.n * model.u.n, t: d.e * front.e + d.n * front.n };
}
/** Keeps the part of a 2-D profile with x ≥ x0 (Sutherland–Hodgman against one line). */
function clipBelowX(poly: [number, number][], x0: number): [number, number][] {
const out: [number, number][] = [];
for (let i = 0; i < poly.length; i++) {
const p = poly[i];
const q = poly[(i + 1) % poly.length];
if (p[0] >= x0) out.push(p);
if ((p[0] - x0) * (q[0] - x0) < 0) {
const y = p[1] + ((q[1] - p[1]) * (x0 - p[0])) / (q[0] - p[0]);
out.push([x0, y]);
}
}
return out;
}
function wingDirectionAxis(model: AbutModel): { ws: number; wt: number } {
const rad = ((model.wing?.lineDeg ?? 0) * Math.PI) / 180;
const w = { e: Math.cos(rad), n: Math.sin(rad) };
@@ -207,7 +222,8 @@ export function buildAbutGroup(
[innerS(L, slabFar), slabFar],
[innerS(0, slabFar), slabFar],
],
(s, t) => bracketTopEl(s) + SLAB_FALL * (t - slabNear),
// Flat on the bracket where it lies over the back strip (t ≥ 벽체 배면), falling 4 % only behind the wall.
(s, t) => bracketTopEl(s) - SLAB_FALL * Math.max(0, members.shape.wallBackM - t),
),
new THREE.MeshStandardMaterial({ color: 0xd8c8a0, roughness: 0.9, side: THREE.DoubleSide }),
);
@@ -239,7 +255,8 @@ export function buildAbutGroup(
...[...wing.bottomLine].reverse().map((p) => [p.dM, p.el] as [number, number]),
];
const ax = new THREE.Vector3(dir.ws, 0, dir.wt);
const geom = prism(profile, 0.45, new THREE.Vector3(sEnd, 0, 0), ax, ey, new THREE.Vector3(a, 0, 0));
// The wing starts behind the 벽체 back face (t = 벽체 배면 −0.8): the part in front of it lies inside the 벽체 and 흉벽, so it is cut off.
const geom = prism(clipBelowX(profile, members.shape.wallBackM / dir.wt), 0.45, new THREE.Vector3(sEnd, 0, 0), ax, ey, new THREE.Vector3(a, 0, 0));
add('wings', geom, 0xc9a77c);
for (const h of wing.haunches) {
const shape: [number, number][] = h.corners.map((c) => {