From 824ea6f494e35ae71a583ee2e3df278ece3d2e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 12 Mar 2026 22:56:34 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[pmis]=20=EB=B0=A9=ED=99=94=EC=85=94?= =?UTF-8?q?=ED=84=B0=EB=A5=BC=20Jamb+=ED=95=98=EC=9A=B0=EC=A7=95+=ED=8C=A8?= =?UTF-8?q?=EB=84=90=20=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 내화시간 1시간→2시간으로 변경 - 좌/우 Jamb(문틀) + 상부 하우징(셔터박스) + 닫힌 패널 4파트 구조 - Jamb/하우징은 불투명, 셔터 패널만 반투명 빨간색 - 요소 통계에서 패널만 카운트 (부품 제외) - 선택 시 부위/구역 정보 표시 추가 --- resources/views/juil/bim-viewer.blade.php | 54 ++++++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/resources/views/juil/bim-viewer.blade.php b/resources/views/juil/bim-viewer.blade.php index 0d1be5b8..d5f69764 100644 --- a/resources/views/juil/bim-viewer.blade.php +++ b/resources/views/juil/bim-viewer.blade.php @@ -331,7 +331,7 @@ class BimScene { } box(gx, gy, gz, pos, userData, group) { - const isTransparent = userData.type === 'window' || userData.type === 'fireShutter'; + const isTransparent = userData.type === 'window' || userData.transparent; const mesh = new THREE.Mesh(new THREE.BoxGeometry(gx, gy, gz), this.mat(COLORS[userData.type], isTransparent)); mesh.position.set(pos[0], pos[1], pos[2]); mesh.castShadow = true; mesh.receiveShadow = true; mesh.userData = userData; @@ -393,16 +393,42 @@ class BimScene { }); this.box(sw - 0.4, 0.15, sd - 0.4, [sx, baseY + wh / 2, sz], { type: 'stair', name: `STAIR-LAND-${f + 1}F`, material: '철근콘크리트', floor: `${f + 1}F` }, 'stair'); } - // 방화셔터 — 계단실 입구 + 방화구획 경계 + // 방화셔터 — Jamb(문틀) + 상부하우징 + 닫힌 셔터패널 + const JW = 0.2, JD = 0.25, HH = 0.35; // Jamb 폭/깊이, Housing 높이 + const fsCommon = { type: 'fireShutter', material: '방화셔터 (내화 2시간)' }; for (let f = 0; f < NF; f++) { const baseY = f * FH + 0.3, sh = FH - 0.6; - // 계단실 입구 방화셔터 (X방향 개구부) - this.box(0.08, sh, sd - 0.6, [sx - sw / 2 - 0.1, baseY + sh / 2, sz], - { type: 'fireShutter', name: `FS-STAIR-${f + 1}F`, material: '방화셔터 (내화 1시간)', floor: `${f + 1}F`, dimensions: `${(sd - 0.6).toFixed(1)}m × ${sh.toFixed(1)}m`, zone: '계단실 입구' }, 'fireShutter'); - // 방화구획 경계 셔터 (X=20m, 40m 지점 — 건물을 3개 방화구획으로 분리) + // ── 계단실 입구 방화셔터 (Z방향 개구부) ── + const stX = sx - sw / 2 - 0.1, stW = sd - 0.6; + const stZ1 = sz - stW / 2, stZ2 = sz + stW / 2; + // 좌 Jamb + this.box(JD, sh, JW, [stX, baseY + sh / 2, stZ1 - JW / 2], + { ...fsCommon, name: `FS-STAIR-${f + 1}F-JL`, floor: `${f + 1}F`, part: '좌측 Jamb' }, 'fireShutter'); + // 우 Jamb + this.box(JD, sh, JW, [stX, baseY + sh / 2, stZ2 + JW / 2], + { ...fsCommon, name: `FS-STAIR-${f + 1}F-JR`, floor: `${f + 1}F`, part: '우측 Jamb' }, 'fireShutter'); + // 상부 하우징 (셔터박스) + this.box(JD + 0.1, HH, stW + JW * 2, [stX, baseY + sh - HH / 2, sz], + { ...fsCommon, name: `FS-STAIR-${f + 1}F-BOX`, floor: `${f + 1}F`, part: '상부 하우징' }, 'fireShutter'); + // 셔터 패널 (닫힘 상태, 반투명) + this.box(0.06, sh - HH, stW, [stX, baseY + (sh - HH) / 2, sz], + { ...fsCommon, name: `FS-STAIR-${f + 1}F`, floor: `${f + 1}F`, transparent: true, dimensions: `${stW.toFixed(1)}m × ${(sh - HH).toFixed(1)}m`, zone: '계단실 입구' }, 'fireShutter'); + + // ── 방화구획 경계 셔터 (Z방향, X=20m/40m) ── [20, 40].forEach((zoneX, zi) => { - this.box(0.08, sh, D - 1, [zoneX, baseY + sh / 2, D / 2], - { type: 'fireShutter', name: `FS-ZONE${zi + 1}-${f + 1}F`, material: '방화셔터 (내화 1시간)', floor: `${f + 1}F`, dimensions: `${D - 1}m × ${sh.toFixed(1)}m`, zone: `방화구획 ${zi + 1}구간 경계` }, 'fireShutter'); + const zW = D - 1, zZ1 = D / 2 - zW / 2, zZ2 = D / 2 + zW / 2; + // 좌 Jamb + this.box(JD, sh, JW, [zoneX, baseY + sh / 2, zZ1 - JW / 2], + { ...fsCommon, name: `FS-ZONE${zi + 1}-${f + 1}F-JL`, floor: `${f + 1}F`, part: '좌측 Jamb' }, 'fireShutter'); + // 우 Jamb + this.box(JD, sh, JW, [zoneX, baseY + sh / 2, zZ2 + JW / 2], + { ...fsCommon, name: `FS-ZONE${zi + 1}-${f + 1}F-JR`, floor: `${f + 1}F`, part: '우측 Jamb' }, 'fireShutter'); + // 상부 하우징 + this.box(JD + 0.1, HH, zW + JW * 2, [zoneX, baseY + sh - HH / 2, D / 2], + { ...fsCommon, name: `FS-ZONE${zi + 1}-${f + 1}F-BOX`, floor: `${f + 1}F`, part: '상부 하우징' }, 'fireShutter'); + // 셔터 패널 (닫힘, 반투명) + this.box(0.06, sh - HH, zW, [zoneX, baseY + (sh - HH) / 2, D / 2], + { ...fsCommon, name: `FS-ZONE${zi + 1}-${f + 1}F`, floor: `${f + 1}F`, transparent: true, dimensions: `${zW}m × ${(sh - HH).toFixed(1)}m`, zone: `방화구획 ${zi + 1}구간 경계` }, 'fireShutter'); }); } } @@ -561,7 +587,11 @@ class BimScene { getCounts() { if (this.mode === 'ifc') return this.ifcCounts; const c = {}; - this.meshes.forEach(m => { const t = m.userData.type; if (t) c[t] = (c[t] || 0) + 1; }); + this.meshes.forEach(m => { + const t = m.userData.type; if (!t) return; + if (t === 'fireShutter' && m.userData.part) return; // Jamb/Housing 제외, 패널만 카운트 + c[t] = (c[t] || 0) + 1; + }); return c; } @@ -789,9 +819,9 @@ function BimInfoPanel({ selected, counts, mode, ifcModelInfo }) { {k}{v} )) ) : ( - [['유형', TYPE_LABEL[selected.type] || selected.type], ['재질', selected.material], - ['층', selected.floor], ['치수', selected.dimensions], ['면', selected.face], - ['방향', selected.direction], ['격자', selected.grid], + [['유형', TYPE_LABEL[selected.type] || selected.type], ['부위', selected.part], + ['재질', selected.material], ['층', selected.floor], ['치수', selected.dimensions], + ['구역', selected.zone], ['면', selected.face], ['방향', selected.direction], ['격자', selected.grid], ].filter(([, v]) => v).map(([k, v]) => ( {k}{v} ))