7、三维机械设计、装配与运动仿真组件 - /设计与仿真组件/3d-mechanical-designer

时间:2025-05-15 09:11:23
// 三维机械设计、装配与运动仿真通用组件 // 初始化Three.js相关变量 let scene, camera, renderer, controls, transformControls, raycaster, mouse; let robot = { parts: [], joints: [] }; let selectedObject = null; let clock = new THREE.Clock(); let isSimulating = false; let keyframes = []; let currentTime = 0; // 页面加载完成后初始化应用 document.addEventListener('DOMContentLoaded', () => { initScene(); initEventListeners(); createExampleRobot(); // 初始化完成后隐藏加载动画(如果有) const loadingOverlay = document.querySelector('.loading-overlay'); if (loadingOverlay) { loadingOverlay.style.display = 'none'; } }); // 初始化场景 function initScene() { // 创建场景 scene = new THREE.Scene(); scene.background = new THREE.Color(0xf8f8f8); // 添加网格 const gridHelper = new THREE.GridHelper(10, 10, 0xcccccc, 0xe0e0e0); scene.add(gridHelper); // 添加环境光 const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambientLight); // 添加定向光 const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(5, 10, 7.5); directionalLight.castShadow = true; scene.add(directionalLight); // 设置相机 const canvas = document.getElementById('robot-canvas'); const aspect = canvas.clientWidth / canvas.clientHeight; camera = new THREE.PerspectiveCamera(45, aspect, 0.1, 1000); camera.position.set(5, 5, 5); camera.lookAt(new THREE.Vector3(0, 0, 0)); // 设置渲染器 renderer = new THREE.WebGLRenderer({ canvas, antialias: true }); renderer.setSize(canvas.clientWidth, canvas.clientHeight); renderer.setPixelRatio(window.devicePixelRatio); renderer.shadowMap.enabled = true; // 添加轨道控制器 controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.25; // 添加变换控制器 transformControls = new THREE.TransformControls(camera, renderer.domElement); transformControls.addEventListener('dragging-changed', (event) => { controls.enabled = !event.value; }); scene.add(transformControls); // 创建射线投射器,用于选择对象 raycaster = new THREE.Raycaster(); mouse = new THREE.Vector2(); // 窗口大小变化时更新渲染器尺寸 window.addEventListener('resize', onWindowResize); // 开始渲染循环 animate(); } // 窗口大小变化事件处理器 function onWindowResize() { const canvas = renderer.domElement; const width = canvas.clientWidth; const height = canvas.clientHeight; camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setSize(width, height, false); } // 渲染循环 function animate() { requestAnimationFrame(animate); // 更新轨道控制器 controls.update(); // 如果正在仿真,执行运动仿真 if (isSimulating) { updateSimulation(); } // 更新坐标显示 updateCoordinatesDisplay(); // 更新FPS显示 updateFPS(); // 渲染场景 renderer.render(scene, camera); } // 更新坐标显示 function updateCoordinatesDisplay() { const cameraPosition = camera.position; document.getElementById('coordinates-display').textContent = `X: ${cameraPosition.x.toFixed(2)} Y: ${cameraPosition.y.toFixed(2)} Z: ${cameraPosition.z.toFixed(2)}`; } // 更新FPS显示 let frameCount = 0; let prevTime = performance.now(); function updateFPS() { frameCount++; const currentTime = performance.now(); if (currentTime - prevTime >= 1000) { const fps = Math.round((frameCount * 1000) / (currentTime - prevTime)); document.getElementById('fps-display').textContent = `FPS: ${fps}`; frameCount = 0; prevTime = currentTime; } } // 添加机器人关节 function addJoint(type, position) { const geometry = new THREE.SphereGeometry(0.2, 32, 32); const material = new THREE.MeshStandardMaterial({ color: 0xff5722 }); const joint = new THREE.Mesh(geometry, material); joint.position.copy(position || new THREE.Vector3(0, 0, 0)); joint.userData.isJoint = true; joint.userData.type = type || 'revolute'; joint.userData.range = 90; joint.userData.speed = 1; joint.userData.angle = 0; scene.add(joint); robot.joints.push(joint); updateModelInfo(); return joint; } // 添加机器人连杆 function addLink(startJoint, endJoint) { if (!startJoint || !endJoint) return null; // 计算连杆位置和方向 const start = startJoint.position.clone(); const end = endJoint.position.clone(); const direction = end.clone().sub(start); const length = direction.length(); // 创建连杆几何体 const geometry = new THREE.CylinderGeometry(0.1, 0.1, length, 16); geometry.translate(0, length / 2, 0); // 创建连杆材质 const material = new THREE.MeshStandardMaterial({ color: 0x2196F3 }); const link = new THREE.Mesh(geometry, material); // 设置连杆位置和旋转 link.position.copy(start); link.lookAt(end); link.rotateX(Math.PI / 2); // 设置连杆的自定义数据 link.userData.isLink = true; link.userData.startJoint = startJoint; link.userData.endJoint = endJoint; scene.add(link); robot.parts.push(link); updateModelInfo(); return link; } // 更新模型信息显示 function updateModelInfo() { document.getElementById('model-info').textContent = `零件: ${robot.parts.length} | 关节: ${robot.joints.length}`; } // 选择对象 function selectObject(object) { if (selectedObject === object) return; // 取消先前选择的对象 if (selectedObject) { if (selectedObject.material && selectedObject.material.emissive) { selectedObject.material.emissive.setHex(0x000000); } } // 设置新选择的对象 selectedObject = object; if (selectedObject) { // 高亮选中的对象 if (selectedObject.material && selectedObject.material.emissive) { selectedObject.material.emissive.setHex(0x333333); } // 应用变换控制器 transformControls.attach(selectedObject); // 更新属性面板 updatePropertiesPanel(); } else { transformControls.detach(); document.getElementById('selected-item-name').textContent = '无'; } } // 更新属性面板 function updatePropertiesPanel() { if (!selectedObject) return; // 更新选中项目名称 let itemName = selectedObject.userData.isJoint ? '关节' : selectedObject.userData.isLink ? '连杆' : '物体'; document.getElementById('selected-item-name').textContent = itemName; // 更新位置 document.getElementById('position-x').value = selectedObject.position.x.toFixed(2); document.getElementById('position-y').value = selectedObject.position.y.toFixed(2); document.getElementById('position-z').value = selectedObject.position.z.toFixed(2); // 更新旋转(转换为度数) const rotation = selectedObject.rotation.toArray().slice(0, 3).map(rad => (rad * 180 / Math.PI).toFixed(0)); document.getElementById('rotation-x').value = rotation[0]; document.getElementById('rotation-y').value = rotation[1]; document.getElementById('rotation-z').value = rotation[2]; // 更新尺寸 document.getElementById('scale').value = selectedObject.scale.x.toFixed(1); // 更新材质颜色 if (selectedObject.material && selectedObject.material.color) { const color = '#' + selectedObject.material.color.getHexString(); document.getElementById('material-color').value = color; } // 更新关节特有的属性 const jointProperties = document.querySelectorAll('.joint-property'); if (selectedObject.userData.isJoint) { jointProperties.forEach(el => el.style.display = 'flex'); // 更新关节类型 document.getElementById('joint-type').value = selectedObject.userData.type || 'revolute'; // 更新关节范围 document.getElementById('joint-range').value = selectedObject.userData.range || 90; document.getElementById('joint-range-value').textContent = `${selectedObject.userData.range || 90}°`; // 更新关节速度 document.getElementById('joint-speed').value = selectedObject.userData.speed || 1; } else { jointProperties.forEach(el => el.style.display = 'none'); } } // 更新模型参数 function updateModelParameters() { if (!selectedObject) return; // 更新位置 selectedObject.position.set( parseFloat(document.getElementById('position-x').value), parseFloat(document.getElementById('position-y').value), parseFloat(document.getElementById('position-z').value) ); // 更新旋转(转换为弧度) selectedObject.rotation.set( parseFloat(document.getElementById('rotation-x').value) * Math.PI / 180, parseFloat(document.getElementById('rotation-y').value) * Math.PI / 180, parseFloat(document.getElementById('rotation-z').value) * Math.PI / 180 ); // 更新尺寸 const scale = parseFloat(document.getElementById('scale').value); selectedObject.scale.set(scale, scale, scale); // 更新材质颜色 if (selectedObject.material && selectedObject.material.color) { const color = document.getElementById('material-color').value; selectedObject.material.color.set(color); } // 对于关节,更新特有属性 if (selectedObject.userData.isJoint) { selectedObject.userData.type = document.getElementById('joint-type').value; selectedObject.userData.range = parseInt(document.getElementById('joint-range').value); selectedObject.userData.speed = parseFloat(document.getElementById('joint-speed').value); document.getElementById('joint-range-value').textContent = `${selectedObject.userData.range}°`; } // 如果是连杆,可能需要更新其几何形状 if (selectedObject.userData.isLink) { updateLinkGeometry(selectedObject); } } // 更新连杆几何形状 function updateLinkGeometry(link) { if (