부재 컨트롤 패널 추가 - 온오프 및 줌핏 기능
기능 추가: 1. 패널에 부재 구성 표시 ✓ 부재명 (한글) ✓ 규격 정보 (mm 단위) ✓ 부재 개수 ✓ 색상 표시 2. 부재 온오프 제어 ✓ 부재 타입별 체크박스 ✓ 체크 해제하면 해당 부재 숨김 ✓ 실시간 표시/숨김 3. 줌핏 기능 ✓ "🎯 줌핏" 버튼 추가 ✓ 활성화된 부재만 경계 계산 ✓ 자동 카메라 조정 ✓ 선택된 부재들이 화면 중앙에 표시 부재 그룹 관리: - memberGroups 객체로 부재 타입별 메시 추적 - 메시 userData에 groupName 저장 - 체크박스와 메시 가시성 연동 사용법: 1. 패널의 체크박스로 부재 표시/숨김 2. "줌핏" 버튼으로 선택된 부재에 자동 줌 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7031932edd
commit
d2ad629213
1 file changed
+124
-6
+124
-6
@@ -191,13 +191,39 @@
|
||||
|
||||
<div class="section">
|
||||
<h3>🏗️ 부재 구성</h3>
|
||||
<div style="font-size: 10px; line-height: 1.5; color: #cbd5e0;">
|
||||
<div style="margin-bottom: 4px;"><strong style="color: #ff6b6b;">가시설4</strong> 52개</div>
|
||||
<div style="margin-bottom: 4px;"><strong style="color: #7ed321;">h-pile2</strong> 244개</div>
|
||||
<div style="margin-bottom: 4px;"><strong style="color: #4a90e2;">토류판</strong> 240개</div>
|
||||
<div style="margin-bottom: 4px;"><strong style="color: #7ed321;">H-PILE</strong> 26개</div>
|
||||
<div><strong style="color: #f5a623;">스터럿</strong> 26개</div>
|
||||
<div style="font-size: 9px; line-height: 1.8; color: #cbd5e0;">
|
||||
<label style="display: flex; align-items: center; margin-bottom: 4px; cursor: pointer;">
|
||||
<input type="checkbox" id="cb-gasiseol4" checked style="margin-right: 6px; cursor: pointer;">
|
||||
<span style="color: #ff6b6b;">■</span>
|
||||
<span style="margin-left: 4px;"><strong>가시설4</strong> (H-400×400)</span>
|
||||
<span style="margin-left: auto; color: #10b981;">52개</span>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; margin-bottom: 4px; cursor: pointer;">
|
||||
<input type="checkbox" id="cb-hpile2" checked style="margin-right: 6px; cursor: pointer;">
|
||||
<span style="color: #7ed321;">■</span>
|
||||
<span style="margin-left: 4px;"><strong>h-pile2</strong> (φ1000×8000)</span>
|
||||
<span style="margin-left: auto; color: #10b981;">244개</span>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; margin-bottom: 4px; cursor: pointer;">
|
||||
<input type="checkbox" id="cb-tolyupan" checked style="margin-right: 6px; cursor: pointer;">
|
||||
<span style="color: #4a90e2;">■</span>
|
||||
<span style="margin-left: 4px;"><strong>토류판</strong> (PL-700×360×14)</span>
|
||||
<span style="margin-left: auto; color: #10b981;">240개</span>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; margin-bottom: 4px; cursor: pointer;">
|
||||
<input type="checkbox" id="cb-hpile" checked style="margin-right: 6px; cursor: pointer;">
|
||||
<span style="color: #7ed321;">■</span>
|
||||
<span style="margin-left: 4px;"><strong>H-PILE</strong> (H-400×400)</span>
|
||||
<span style="margin-left: auto; color: #10b981;">26개</span>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||||
<input type="checkbox" id="cb-strut" checked style="margin-right: 6px; cursor: pointer;">
|
||||
<span style="color: #f5a623;">■</span>
|
||||
<span style="margin-left: 4px;"><strong>스터럿</strong> (200×200×4000)</span>
|
||||
<span style="margin-left: auto; color: #10b981;">26개</span>
|
||||
</label>
|
||||
</div>
|
||||
<button id="btnZoomFit" style="width: 100%; margin-top: 8px; padding: 6px; background: #10b981; color: #1a1f2e; border: none; border-radius: 4px; font-size: 10px; font-weight: 700; cursor: pointer;">🎯 줌핏</button>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
@@ -338,12 +364,23 @@
|
||||
|
||||
const DISPLAY_SCALE = 0.01;
|
||||
|
||||
// 부재 그룹 추적 (온오프 제어용)
|
||||
const memberGroups = {
|
||||
'가시설4': [],
|
||||
'h-pile2': [],
|
||||
'토류판': [],
|
||||
'H-PILE': [],
|
||||
'스터럿': []
|
||||
};
|
||||
|
||||
// 샘플 부재 (테스트용)
|
||||
const testGeo = new THREE.BoxGeometry(4, 4, 80);
|
||||
const testMat = new THREE.MeshStandardMaterial({ color: 0x7ed321, roughness: 0.7, emissive: 0x2d5a2d });
|
||||
const testMesh = new THREE.Mesh(testGeo, testMat);
|
||||
testMesh.position.set(0, -2554, 100);
|
||||
testMesh.userData.groupName = 'h-pile2';
|
||||
scene.add(testMesh);
|
||||
memberGroups['h-pile2'].push(testMesh);
|
||||
console.log('✓ 샘플 부재 추가됨');
|
||||
|
||||
// 그리드 & 축
|
||||
@@ -426,7 +463,14 @@
|
||||
|
||||
const mesh = new THREE.Mesh(geometry, getMaterial(type));
|
||||
mesh.position.set(position.x, position.y, position.z + 10);
|
||||
mesh.userData = { blockType, type, spec, volume, groupName: blockType };
|
||||
scene.add(mesh);
|
||||
|
||||
// 부재 그룹에 추가
|
||||
if (memberGroups[blockType]) {
|
||||
memberGroups[blockType].push(mesh);
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
if (count <= 3 || count % 100 === 0) {
|
||||
@@ -447,6 +491,80 @@
|
||||
document.getElementById('loadingMsg').innerHTML = '❌ 로드 실패<br>' + err.message;
|
||||
});
|
||||
|
||||
// 부재 토글 기능
|
||||
const checkboxes = {
|
||||
'가시설4': document.getElementById('cb-gasiseol4'),
|
||||
'h-pile2': document.getElementById('cb-hpile2'),
|
||||
'토류판': document.getElementById('cb-tolyupan'),
|
||||
'H-PILE': document.getElementById('cb-hpile'),
|
||||
'스터럿': document.getElementById('cb-strut')
|
||||
};
|
||||
|
||||
Object.entries(checkboxes).forEach(([name, checkbox]) => {
|
||||
checkbox.addEventListener('change', (e) => {
|
||||
const isVisible = e.target.checked;
|
||||
memberGroups[name].forEach(mesh => {
|
||||
mesh.visible = isVisible;
|
||||
});
|
||||
console.log(`${name}: ${isVisible ? '표시' : '숨김'}`);
|
||||
});
|
||||
});
|
||||
|
||||
// 줌핏 기능
|
||||
document.getElementById('btnZoomFit').addEventListener('click', () => {
|
||||
// 활성화된 부재들의 경계 계산
|
||||
let minX = Infinity, maxX = -Infinity;
|
||||
let minY = Infinity, maxY = -Infinity;
|
||||
let minZ = Infinity, maxZ = -Infinity;
|
||||
let hasVisible = false;
|
||||
|
||||
Object.entries(memberGroups).forEach(([name, meshes]) => {
|
||||
if (checkboxes[name].checked) {
|
||||
meshes.forEach(mesh => {
|
||||
if (mesh.geometry.boundingBox === null) {
|
||||
mesh.geometry.computeBoundingBox();
|
||||
}
|
||||
const box = mesh.geometry.boundingBox;
|
||||
const pos = mesh.position;
|
||||
|
||||
minX = Math.min(minX, pos.x + box.min.x);
|
||||
maxX = Math.max(maxX, pos.x + box.max.x);
|
||||
minY = Math.min(minY, pos.y + box.min.y);
|
||||
maxY = Math.max(maxY, pos.y + box.max.y);
|
||||
minZ = Math.min(minZ, pos.z + box.min.z);
|
||||
maxZ = Math.max(maxZ, pos.z + box.max.z);
|
||||
hasVisible = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasVisible) {
|
||||
console.log('표시된 부재가 없습니다');
|
||||
return;
|
||||
}
|
||||
|
||||
// 경계의 중심과 크기 계산
|
||||
const center = new THREE.Vector3(
|
||||
(minX + maxX) / 2,
|
||||
(minY + maxY) / 2,
|
||||
(minZ + maxZ) / 2
|
||||
);
|
||||
|
||||
const size = new THREE.Vector3(
|
||||
maxX - minX,
|
||||
maxY - minY,
|
||||
maxZ - minZ
|
||||
);
|
||||
|
||||
const maxDim = Math.max(size.x, size.y, size.z);
|
||||
const fov = camera.fov * (Math.PI / 180);
|
||||
let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2));
|
||||
|
||||
camera.position.set(center.x, center.y, center.z + cameraZ * 1.5);
|
||||
camera.lookAt(center);
|
||||
console.log('✓ 줌핏 완료');
|
||||
});
|
||||
|
||||
// 렌더링
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
Reference in new issue
Block a user