fix(abut): 접속슬래브를 날개벽 안쪽면 사이에 두어 간섭 제거
- 슬래브 평면을 날개벽 안쪽면에서 20 mm 안쪽으로 잡음 (spec §4.8 "측면 = 날개벽 안쪽면 + 20"). 날개벽은 축 방향 프리즘(두께 0.45 m, s 방향)으로 그리므로 안쪽면 s = sEnd + a·0.45 + (ws/wt)·t - 배면 끝은 흉벽 배면 −0.505에서 7 m (t 기준) - 간섭 검사 테스트: 슬래브 꼭짓점이 어느 날개벽 평면 영역에도 들어가지 않음 - 테스트 635건 통과, 타입 검사·빌드 통과. 브라우저 시각 확인은 미실시 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8f75c6dc9f
commit
fbc315fdba
2 files changed
+54
-17
No files matched your search
+28
-10
@@ -8,7 +8,7 @@
|
||||
import * as THREE from 'three';
|
||||
import type { AbutModel, AbutPoint } from '../core/abut/geometry';
|
||||
import { buildMembers, type AbutMembers } from '../core/abut/members';
|
||||
import { buildWings, type WingSolid } from '../core/abut/wings';
|
||||
import { buildWings, WING_THICKNESS_M, type WingSolid } from '../core/abut/wings';
|
||||
import type { TerrainGrid } from '../core/ground-model';
|
||||
import { terrainGeometry } from './terrain';
|
||||
|
||||
@@ -19,15 +19,16 @@ const SLAB_THICKNESS_M = 0.45;
|
||||
const SLAB_LENGTH_M = 7;
|
||||
/** 접속슬래브 하향 경사 (배면 쪽으로), spec_abut §4.8 "하향 4 %". */
|
||||
const SLAB_FALL = 0.04;
|
||||
/** 접속슬래브와 날개벽 안쪽면 사이 간격 (m), spec_abut §4.8 "날개벽 20". */
|
||||
const SLAB_CLEARANCE_M = 0.02;
|
||||
|
||||
/** Eight-corner slab with its top sloping away from the wall. Indexed by (s end, t end, bottom/top). Double-sided: the winding is not critical. */
|
||||
function slabGeometry(s0: number, s1: number, tNear: number, tFar: number, bottomEl: (s: number, t: number) => number): THREE.BufferGeometry {
|
||||
const corners = [s0, s1].flatMap((s) => [tNear, tFar].flatMap((t) => [bottomEl(s, t), bottomEl(s, t) + SLAB_THICKNESS_M].map((y) => [s, y, t])));
|
||||
// index = is * 4 + it * 2 + ih (ih 0 = bottom, 1 = top)
|
||||
function slabGeometry(plan: [number, number][], bottomEl: (s: number, t: number) => number): THREE.BufferGeometry {
|
||||
// plan: four (s, t) corners in order; vertices 0–3 are the bottom, 4–7 the top.
|
||||
const corners = [...plan.map(([s, t]) => [s, bottomEl(s, t), t]), ...plan.map(([s, t]) => [s, bottomEl(s, t) + SLAB_THICKNESS_M, t])];
|
||||
const quads = [
|
||||
[0, 2, 3, 1], [4, 5, 7, 6], // s0 and s1 ends
|
||||
[0, 4, 6, 2], [1, 3, 7, 5], // bottom and top
|
||||
[0, 1, 5, 4], [2, 6, 7, 3], // near and far faces (t ends)
|
||||
[0, 1, 2, 3], [4, 5, 6, 7], // bottom and top
|
||||
[0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7], // sides
|
||||
];
|
||||
const positions: number[] = [];
|
||||
const indices: number[] = [];
|
||||
@@ -173,12 +174,29 @@ export function buildAbutGroup(
|
||||
}
|
||||
|
||||
// 접속슬래브 (spec §4.8): bottom on the 브라켓 top (흉벽 상단 − 0.85), 5 mm behind the 흉벽 back face, 7 m back, falling 4 % away from the wall.
|
||||
// Centred on the axis over the wing-to-wing width (slabWidthMm); the 흉벽 slope along s is followed.
|
||||
const slabW = (model.wing?.slabWidthMm ?? L * 1000) / 1000;
|
||||
// Plan: between the wing inner faces, each 20 mm inside (spec §4.8 "측면 = 날개벽 안쪽면 + 20"), so it never meets a wing.
|
||||
// The wing is drawn as a prism over the wing direction with its 0.45 m thickness along s, so its inner face at t is s = sEnd + a·0.45 + (ws/wt)·t.
|
||||
const slabNear = members.shape.parapetBackM - 0.005;
|
||||
const slabFar = slabNear - SLAB_LENGTH_M;
|
||||
const innerS = (sEnd: number, t: number): number => {
|
||||
const w = wings.find((x) => x.sEnd === sEnd);
|
||||
if (!w) return sEnd; // no wing on this side: the slab ends at the 교좌면 끝선
|
||||
const a = w.sEnd === 0 ? 1 : -1;
|
||||
const p0 = toAxis(model, w.corners[0]);
|
||||
const p1 = toAxis(model, w.corners[1]);
|
||||
return sEnd + a * (WING_THICKNESS_M + SLAB_CLEARANCE_M) + ((p1.s - p0.s) / (p1.t - p0.t)) * t;
|
||||
};
|
||||
const bracketTopEl = (s: number) => members.parapetTopStartEl + ((members.parapetTopEndEl - members.parapetTopStartEl) * s) / L - members.shape.bracketHeightM;
|
||||
const slabMesh = new THREE.Mesh(
|
||||
slabGeometry((L - slabW) / 2, (L + slabW) / 2, slabNear, slabNear - SLAB_LENGTH_M, (s, t) => bracketTopEl(s) + SLAB_FALL * (t - slabNear)),
|
||||
slabGeometry(
|
||||
[
|
||||
[innerS(0, slabNear), slabNear],
|
||||
[innerS(L, slabNear), slabNear],
|
||||
[innerS(L, slabFar), slabFar],
|
||||
[innerS(0, slabFar), slabFar],
|
||||
],
|
||||
(s, t) => bracketTopEl(s) + SLAB_FALL * (t - slabNear),
|
||||
),
|
||||
new THREE.MeshStandardMaterial({ color: 0xd8c8a0, roughness: 0.9, side: THREE.DoubleSide }),
|
||||
);
|
||||
slabMesh.name = 'slab';
|
||||
|
||||
Reference in new issue
Block a user