11、参数化三维产品设计组件 - /设计与仿真组件/parametric-3d-product-design
76个工业组件库示例汇总
参数化三维产品设计组件 (注塑模具与公差分析)
概述
这是一个交互式的 Web 组件,旨在演示简单的三维零件(如带凸台的方块)的参数化设计过程,并结合注塑模具设计(如开模动画)与公差分析(如可视化公差带)的概念性可视化。
主要功能
- 参数化零件建模:
- 通过滑块实时修改零件的基础尺寸(宽度、高度、深度)。
- 通过滑块实时修改特征(圆柱凸台)的尺寸(直径、高度)和相对位置。
- 实时 3D 可视化:
- 使用 Three.js 渲染零件模型。
- 支持通过鼠标进行视图交互(旋转、缩放、平移)。
- 概念性分析与模拟:
- 公差可视化: 切换显示尺寸公差(最小/最大包络盒)和位置公差(特征允许范围)的理论边界。
- 模拟开模: 播放简化的动画,展示模具上下型腔分离的过程。
- 界面与风格:
- 采用苹果科技风格,界面简洁直观。
- 响应式布局,适应不同屏幕尺寸。
如何使用
- 打开页面: 在浏览器中打开
index.html
文件。 - 调整参数:
- 在左侧面板的 “基础零件尺寸” 和 “特征: 圆柱凸台” 部分,拖动滑块调整零件的几何参数。
- 3D 模型会在释放滑块后更新。
- 交互视图:
- 在右侧 3D 视图区,按住鼠标 左键拖动 进行旋转。
- 滚动鼠标滚轮 进行缩放。
- 按住鼠标 右键 (或 Ctrl/Cmd + 左键) 拖动 进行平移。
- 公差分析 (概念):
- 在 “公差分析” 部分调整公差值滑块。
- 点击 “显示/隐藏公差范围” 按钮,切换显示红/绿/蓝色透明几何体,表示理论上的公差带。
- 模拟开模 (概念):
- 点击 “模拟开模 (概念)” 按钮,观看模具分离动画(零件会暂时隐藏)。
- 再次点击(按钮变为 “复位模具”) 可观看模具闭合动画,并重新显示零件。
- 重置视图: 点击 “重置视图” 按钮,将相机恢复到默认位置和朝向。
文件结构
parametric-3d-product-design/
├── index.html # HTML 页面结构
├── styles.css # CSS 样式定义
├── script.js # JavaScript 交互与3D逻辑
└── README.md # 本说明文件
技术栈
- HTML5 / CSS3 (Flexbox, CSS Variables)
- JavaScript (ES6+)
- Three.js (r128)
- Three.js OrbitControls
重要提示
- 概念演示: 本组件主要用于演示原理,并非精确的工程工具。
- 公差可视化: 仅为理论边界的概念性展示,不执行 实际的公差叠加或统计分析。
- 开模模拟: 动画效果高度简化,不涉及真实模具的复杂结构(如滑块、顶针、分型面细节等)。
- 几何模型: 零件由简单的几何体构成,未使用 CSG (构造实体几何) 进行精确合并,可能存在视觉穿插。如需精确模型,可考虑引入相关库。
- 性能: 频繁更新参数(尤其在公差可视化开启时)可能影响性能。
效果展示
源码
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>参数化三维产品设计 - 注塑模具与公差分析</title><link rel="stylesheet" href="styles.css"><!-- 引入 Three.js --><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script><!-- 引入 OrbitControls for camera interaction --><script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script><!-- Optional: Add CSG library if needed for complex boolean operations --><!-- <script src="path/to/three-csg.js"></script> -->
</head>
<body><div class="product-design-container"><header class="app-header"><h1>参数化三维产品设计</h1><p>应用于注塑模具设计与公差分析</p></header><div class="main-content-area"><!-- 左侧参数与控制面板 --><aside class="controls-panel"><h2>参数定义 & 分析</h2><div class="parameter-section"><h3>基础零件尺寸</h3><label for="partWidth">宽度 (X):</label><input type="range" id="partWidth" name="partWidth" min="20" max="100" value="50" step="1"><span class="param-value" id="partWidthValue">50</span> mm<label for="partHeight">高度 (Y):</label><input type="range" id="partHeight" name="partHeight" min="10" max="80" value="30" step="1"><span class="param-value" id="partHeightValue">30</span> mm<label for="partDepth">深度 (Z):</label><input type="range" id="partDepth" name="partDepth" min="20" max="100" value="40" step="1"><span class="param-value" id="partDepthValue">40</span> mm</div><div class="parameter-section"><h3>特征: 圆柱凸台 (Boss)</h3><label for="bossDiameter">直径:</label><input type="range" id="bossDiameter" name="bossDiameter" min="5" max="25" value="15" step="0.5"><span class="param-value" id="bossDiameterValue">15.0</span> mm<label for="bossHeight">高度:</label><input type="range" id="bossHeight" name="bossHeight" min="2" max="20" value="10" step="0.5"><span class="param-value" id="bossHeightValue">10.0</span> mm<label for="bossPosX">X 位置 (%):</label> <!-- Position relative to width --><input type="range" id="bossPosX" name="bossPosX" min="10" max="90" value="50" step="1"><span class="param-value" id="bossPosXValue">50</span> %<label for="bossPosZ">Z 位置 (%):</label> <!-- Position relative to depth --><input type="range" id="bossPosZ" name="bossPosZ" min="10" max="90" value="50" step="1"><span class="param-value" id="bossPosZValue">50</span> %</div><div class="parameter-section"><h3>公差分析 (概念)</h3><label for="dimensionTolerance">尺寸公差 (+/-):</label><input type="range" id="dimensionTolerance" name="dimensionTolerance" min="0.0" max="1.0" value="0.1" step="0.05"><span class="param-value" id="dimensionToleranceValue">0.10</span> mm<label for="positionTolerance">位置公差 (圆域 +/-):</label> <!-- Tolerance zone for boss position --><input type="range" id="positionTolerance" name="positionTolerance" min="0.0" max="0.5" value="0.05" step="0.01"><span class="param-value" id="positionToleranceValue">0.05</span> mm</div><div class="action-buttons"><button id="analyzeToleranceButton">显示/隐藏公差范围</button><button id="simulateMoldButton">模拟开模 (概念)</button><button id="resetViewButton">重置视图</button></div><div class="status-display"><h3>状态</h3><p id="statusText">准备就绪。请调整参数。</p></div></aside><!-- 右侧 3D 可视化区域 --><main class="visualization-area"><div id="rendererContainer"></div><!-- Optional: Maybe add overlay for tolerance info --></main></div><footer class="app-footer"><p>注塑模具设计与公差分析辅助组件</p></footer></div><script src="script.js"></script>
</body>
</html>
styles.css
/* styles.css - Parametric 3D Product Design Component */:root {--primary-bg: #ffffff;--secondary-bg: #f5f5f7;--controls-bg: #e8e8ed;--text-primary: #1d1d1f;--text-secondary: #515154;--accent-blue: #007aff;--accent-blue-hover: #005ec4;--border-color: #d2d2d7;--shadow-color: rgba(0, 0, 0, 0.08);--apple-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;--tolerance-color-min: rgba(255, 0, 0, 0.3); /* Red tint for min */--tolerance-color-max: rgba(0, 255, 0, 0.3); /* Green tint for max */
}body {font-family: var(--apple-font);margin: 0;background-color: var(--secondary-bg);color: var(--text-primary);font-size: 14px;line-height: 1.5;overflow-x: hidden;
}.product-design-container {width: 100%;max-width: 100%;min-height: calc(100vh - 40px);display: flex;flex-direction: column;background-color: var(--primary-bg);box-sizing: border-box;
}.app-header {background-color: var(--primary-bg);text-align: center;padding: 15px 20px;border-bottom: 1px solid var(--border-color);
}.app-header h1 {margin: 0 0 5px 0;font-size: 1.8em;font-weight: 600;color: var(--text-primary);
}.app-header p {margin: 0;color: var(--text-secondary);font-size: 0.9em;
}.main-content-area {flex-grow: 1;display: flex;width: 100%;
}.controls-panel {width: 340px; /* Slightly wider for more controls */flex-shrink: 0;background-color: var(--controls-bg);padding: 20px;border-right: 1px solid var(--border-color);overflow-y: auto;box-sizing: border-box;max-height: calc(100vh - 100px); /* Adjust 100px based on header/footer */
}.controls-panel h2 {margin-top: 0;margin-bottom: 25px;font-size: 1.3em;font-weight: 600;color: var(--text-primary);border-bottom: 1px solid #c8c8cc;padding-bottom: 10px;
}.parameter-section {margin-bottom: 25px; /* Slightly less margin */
}.parameter-section h3 {margin-top: 0;margin-bottom: 15px;font-size: 1.0em;font-weight: 600;color: var(--text-secondary);
}label {display: block;margin-bottom: 5px;font-weight: 500;color: var(--text-primary);font-size: 0.95em;
}input[type="range"] {width: 100%;height: 4px;cursor: pointer;appearance: none;background: #dcdce0;border-radius: 4px;outline: none;margin-bottom: 3px; /* Less space */
}
input[type="range"]::-webkit-slider-thumb {appearance: none;width: 16px;height: 16px;background: var(--accent-blue);border-radius: 50%;cursor: pointer;
}
input[type="range"]::-moz-range-thumb {width: 16px;height: 16px;background: var(--accent-blue);border-radius: 50%;cursor: pointer;border: none;
}.param-value {display: inline-block;margin-right: 5px; /* Space before unit */font-size: 0.9em;color: var(--text-secondary);min-width: 30px; /* Align values a bit */text-align: right;
}/* Style the unit text after span */
.parameter-section > span + span {font-size: 0.85em;color: var(--text-secondary);margin-left: -2px; /* Pull unit closer */margin-bottom: 15px;display: inline-block;
}
.parameter-section label + input + span + span {margin-bottom: 15px; /* Add bottom margin after unit */
}select {/* ... (same as previous component) ... */width: 100%;padding: 8px 10px;border: 1px solid var(--border-color);border-radius: 6px;background-color: var(--primary-bg);font-family: inherit;font-size: 0.95em;margin-bottom: 20px;appearance: none;background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%208l5%205%205-5z%22%20fill%3D%22%23515154%22%2F%3E%3C%2Fsvg%3E');background-repeat: no-repeat;background-position: right 10px center;background-size: 12px;
}.action-buttons button {display: block;width: 100%;padding: 10px 15px;margin-bottom: 10px;font-size: 0.95em;font-weight: 500;color: #fff;background-color: var(--accent-blue);border: none;border-radius: 6px;cursor: pointer;text-align: center;transition: background-color 0.2s ease, box-shadow 0.2s ease;
}.action-buttons button:hover {background-color: var(--accent-blue-hover);box-shadow: 0 2px 5px rgba(0, 122, 255, 0.2);
}.action-buttons button:disabled {background-color: #b0b0b5;cursor: not-allowed;box-shadow: none;
}/* Style specific buttons */
#analyzeToleranceButton, #simulateMoldButton {background-color: #5856d6; /* Purple accent */transition: background-color 0.2s ease;
}
#analyzeToleranceButton:hover, #simulateMoldButton:hover {background-color: #4341a0;box-shadow: 0 2px 5px rgba(88, 86, 214, 0.2);;
}#resetViewButton {background-color: #6c757d;
}
#resetViewButton:hover {background-color: #5a6268;box-shadow: none;
}.status-display {margin-top: 20px;padding-top: 15px;border-top: 1px solid #c8c8cc;
}.status-display h3 {margin-top: 0;margin-bottom: 10px;font-size: 1.0em;font-weight: 600;color: var(--text-secondary);
}#statusText {font-size: 0.9em;color: var(--text-primary);min-height: 3em;
}.visualization-area {flex-grow: 1;position: relative;background-color: var(--secondary-bg);overflow: hidden;
}#rendererContainer {position: absolute;top: 0;left: 0;width: 100%;height: 100%;
}.app-footer {text-align: center;padding: 10px 20px;border-top: 1px solid var(--border-color);background-color: var(--primary-bg);color: var(--text-secondary);font-size: 0.85em;
}/* Responsive adjustments */
@media (max-width: 768px) {.main-content-area {flex-direction: column;}.controls-panel {width: 100%;border-right: none;border-bottom: 1px solid var(--border-color);max-height: 60vh; /* Allow a bit more height for controls */}.visualization-area {height: 40vh;min-height: 250px;}.app-header h1 {font-size: 1.5em;}
}
script.js
// script.js - Parametric 3D Product Design Componentdocument.addEventListener('DOMContentLoaded', () => {// --- DOM Elements ---const rendererContainer = document.getElementById('rendererContainer');const partWidthSlider = document.getElementById('partWidth');const partHeightSlider = document.getElementById('partHeight');const partDepthSlider = document.getElementById('partDepth');const bossDiameterSlider = document.getElementById('bossDiameter');const bossHeightSlider = document.getElementById('bossHeight');const bossPosXSlider = document.getElementById('bossPosX');const bossPosZSlider = document.getElementById('bossPosZ');const dimensionToleranceSlider = document.getElementById('dimensionTolerance');const positionToleranceSlider = document.getElementById('positionTolerance');const partWidthValueSpan = document.getElementById('partWidthValue');const partHeightValueSpan = document.getElementById('partHeightValue');const partDepthValueSpan = document.getElementById('partDepthValue');const bossDiameterValueSpan = document.getElementById('bossDiameterValue');const bossHeightValueSpan = document.getElementById('bossHeightValue');const bossPosXValueSpan = document.getElementById('bossPosXValue');const bossPosZValueSpan = document.getElementById('bossPosZValue');const dimensionToleranceValueSpan = document.getElementById('dimensionToleranceValue');const positionToleranceValueSpan = document.getElementById('positionToleranceValue');const analyzeToleranceButton = document.getElementById('analyzeToleranceButton');const simulateMoldButton = document.getElementById('simulateMoldButton');const resetViewButton = document.getElementById('resetViewButton');const statusText = document.getElementById('statusText');// --- Three.js Setup ---let scene, camera, renderer, controls, partGroup, baseMesh, bossMesh;let toleranceVizGroup, dimTolMeshMin, dimTolMeshMax, posTolViz;let moldHalfTop, moldHalfBottom; // For mold simulationlet material, toleranceMaterialMin, toleranceMaterialMax, positionToleranceMaterial;let isToleranceVisible = false;let isMoldOpen = false;let isAnimating = false;// Conversion factor (e.g., if sliders are mm, and Three.js unit is meters)// Let's work directly in 'mm' like units in Three.js for simplicity here.const scaleFactor = 1;function initThreeJS() {// Scenescene = new THREE.Scene();scene.background = new THREE.Color(0xf5f5f7);// Cameraconst aspect = rendererContainer.clientWidth / rendererContainer.clientHeight;camera = new THREE.PerspectiveCamera(50, aspect, 1, 2000); // Adjusted near/farcamera.position.set(100, 80, 150); // Adjusted for 'mm' scalecamera.lookAt(scene.position);// Rendererrenderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(rendererContainer.clientWidth, rendererContainer.clientHeight);renderer.setPixelRatio(window.devicePixelRatio);rendererContainer.appendChild(renderer.domElement);// Lightsconst ambientLight = new THREE.AmbientLight(0xffffff, 0.6);scene.add(ambientLight);const keyLight = new THREE.DirectionalLight(0xffffff, 0.6);keyLight.position.set(-50, 80, 50);scene.add(keyLight);const fillLight = new THREE.DirectionalLight(0xffffff, 0.3);fillLight.position.set(50, 40, -30);scene.add(fillLight);// Controlscontrols = new THREE.OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.1;// Materialsmaterial = new THREE.MeshStandardMaterial({color: 0x99999f, // Lighter gray plasticmetalness: 0.1,roughness: 0.6,polygonOffset: true, // Helps prevent z-fighting with tolerance vizpolygonOffsetFactor: 1,polygonOffsetUnits: 1});// Tolerance Materials (Semi-transparent)toleranceMaterialMin = new THREE.MeshBasicMaterial({color: 0xff0000,transparent: true,opacity: 0.2,side: THREE.DoubleSide,depthWrite: false // Render after main object});toleranceMaterialMax = new THREE.MeshBasicMaterial({color: 0x00ff00,transparent: true,opacity: 0.2,side: THREE.DoubleSide,depthWrite: false});positionToleranceMaterial = new THREE.MeshBasicMaterial({color: 0x0000ff,transparent: true,opacity: 0.3,side: THREE.DoubleSide,depthWrite: false});// Part GrouppartGroup = new THREE.Group();scene.add(partGroup);// Tolerance Visualization GrouptoleranceVizGroup = new THREE.Group();toleranceVizGroup.visible = false;scene.add(toleranceVizGroup);// Initial Part CreationcreatePart();// Start animation loopanimate();}// --- Parametric Part Generation ---function createPart() {// Clear previous part within the groupwhile (partGroup.children.length > 0) {const child = partGroup.children[0];partGroup.remove(child);if (child.geometry) child.geometry.dispose();}// Get parametersconst width = parseFloat(partWidthSlider.value) * scaleFactor;const height = parseFloat(partHeightSlider.value) * scaleFactor;const depth = parseFloat(partDepthSlider.value) * scaleFactor;const bossD = parseFloat(bossDiameterSlider.value) * scaleFactor;const bossH = parseFloat(bossHeightSlider.value) * scaleFactor;const bossPosXRatio = parseFloat(bossPosXSlider.value) / 100;const bossPosZRatio = parseFloat(bossPosZSlider.value) / 100;// Create Base Boxconst baseGeom = new THREE.BoxGeometry(width, height, depth);baseMesh = new THREE.Mesh(baseGeom, material);// Position base so its bottom is at y=0baseMesh.position.y = height / 2;partGroup.add(baseMesh);// Create Boss (Cylinder)const bossGeom = new THREE.CylinderGeometry(bossD / 2, bossD / 2, bossH, 32);bossMesh = new THREE.Mesh(bossGeom, material);// Position Boss on top surface of the base// Calculate position relative to the base centerconst bossX = (bossPosXRatio - 0.5) * width;const bossZ = (bossPosZRatio - 0.5) * depth;bossMesh.position.set(bossX, height + bossH / 2, bossZ); // Y is base height + half boss heightbossMesh.rotation.x = 0; // Ensure cylinder is uprightpartGroup.add(bossMesh);// --- Update Tolerance Visualizations ---// (Do this separately, only when needed, to avoid recreating on every param change)if (isToleranceVisible) {createToleranceVisualization(); // Recreate tolerance based on new nominal part}updateStatus("零件模型已更新。");}// --- Tolerance Visualization ---function createToleranceVisualization() {// Clear previous visualizationwhile (toleranceVizGroup.children.length > 0) {const child = toleranceVizGroup.children[0];toleranceVizGroup.remove(child);if (child.geometry) child.geometry.dispose();// Dispose materials if specific to tolerance viz? No, using shared ones.}const dimTol = parseFloat(dimensionToleranceSlider.value) * scaleFactor;const posTol = parseFloat(positionToleranceSlider.value) * scaleFactor;// Get current nominal dimensionsconst width = parseFloat(partWidthSlider.value) * scaleFactor;const height = parseFloat(partHeightSlider.value) * scaleFactor;const depth = parseFloat(partDepthSlider.value) * scaleFactor;const bossD = parseFloat(bossDiameterSlider.value) * scaleFactor;const bossH = parseFloat(bossHeightSlider.value) * scaleFactor;const bossX = (parseFloat(bossPosXSlider.value)/100 - 0.5) * width;const bossZ = (parseFloat(bossPosZSlider.value)/100 - 0.5) * depth;const bossNominalY = height + bossH / 2;// 1. Dimension Tolerance (Min/Max Boxes for Base) - Conceptualconst baseMinGeom = new THREE.BoxGeometry(width - 2 * dimTol, height - 2 * dimTol, depth - 2 * dimTol);dimTolMeshMin = new THREE.Mesh(baseMinGeom, toleranceMaterialMin);dimTolMeshMin.position.y = (height - 2 * dimTol) / 2; // Adjust position for new heighttoleranceVizGroup.add(dimTolMeshMin);const baseMaxGeom = new THREE.BoxGeometry(width + 2 * dimTol, height + 2 * dimTol, depth + 2 * dimTol);dimTolMeshMax = new THREE.Mesh(baseMaxGeom, toleranceMaterialMax);dimTolMeshMax.position.y = (height + 2 * dimTol) / 2;toleranceVizGroup.add(dimTolMeshMax);// 2. Position Tolerance (Cylindrical Zone for Boss Centerline) - Conceptual// Create a thin cylinder representing the tolerance zone diameterconst posTolGeom = new THREE.CylinderGeometry(posTol, posTol, height + bossH + dimTol * 2, 32); // Height spans base + boss + toleranceposTolViz = new THREE.Mesh(posTolGeom, positionToleranceMaterial);// Position it at the nominal boss X, Z, centered vertically within its heightposTolViz.position.set(bossX, (height + bossH + dimTol*2) / 2, bossZ);toleranceVizGroup.add(posTolViz);updateStatus("公差范围已更新。");}// --- Animation & Rendering Loop ---function animate() {requestAnimationFrame(animate);controls.update();renderer.render(scene, camera);}// --- Event Listeners ---function setupEventListeners() {// Parameter Slidersconst sliders = [partWidthSlider, partHeightSlider, partDepthSlider,bossDiameterSlider, bossHeightSlider, bossPosXSlider, bossPosZSlider,dimensionToleranceSlider, positionToleranceSlider];sliders.forEach(slider => {slider.addEventListener('input', handleSliderInput);slider.addEventListener('change', handleSliderChange); // Update part only on release});// Action ButtonsanalyzeToleranceButton.addEventListener('click', toggleToleranceAnalysis);simulateMoldButton.addEventListener('click', simulateMoldOpening);resetViewButton.addEventListener('click', resetCameraView);// Window Resizewindow.addEventListener('resize', onWindowResize);}function handleSliderInput(event) {const sliderId = event.target.id;const valueSpan = document.getElementById(sliderId + 'Value');let value = parseFloat(event.target.value);let unit = '';// Determine unit and formattingif (sliderId.includes('Tolerance')) {valueSpan.textContent = value.toFixed(2);unit = 'mm';} else if (sliderId.includes('Pos')) {valueSpan.textContent = value.toFixed(0);unit = '%';} else if (sliderId.includes('Diameter') || sliderId.includes('bossHeight')) {valueSpan.textContent = value.toFixed(1);unit = 'mm';} else {valueSpan.textContent = value.toFixed(0);unit = 'mm';}// Find the next sibling span (if exists) to update unit? No, hardcoded in HTML.// Live update for tolerance sliders if tolerance is visibleif (isToleranceVisible && (sliderId.includes('Tolerance') || sliderId.includes('Pos'))) {createToleranceVisualization();}}function handleSliderChange(event) {// Recreate the main part geometry only when slider drag finishesif (!isAnimating && !event.target.id.includes('Tolerance') && !event.target.id.includes('Pos')) {createPart();} else if (!isAnimating && (event.target.id.includes('Pos'))) {// If only position changed, update part and tolerance if visiblecreatePart();}// Tolerance slider changes already handled live in input if visible}// --- Button Actions ---function toggleToleranceAnalysis() {isToleranceVisible = !isToleranceVisible;if (isToleranceVisible) {createToleranceVisualization();toleranceVizGroup.visible = true;analyzeToleranceButton.textContent = "隐藏公差范围";updateStatus("显示公差范围 (概念性)。");} else {toleranceVizGroup.visible = false;analyzeToleranceButton.textContent = "显示公差范围";updateStatus("公差范围已隐藏。");}}function simulateMoldOpening() {if (isAnimating) return;isAnimating = true;simulateMoldButton.disabled = true;resetViewButton.disabled = true;analyzeToleranceButton.disabled = true;// Simple simulation: create two halves and move them apartif (!moldHalfTop || !moldHalfBottom) {// Create conceptual mold halves (simple boxes)const width = parseFloat(partWidthSlider.value) * scaleFactor + 20; // Mold bigger than partconst height = parseFloat(partHeightSlider.value) * scaleFactor / 2 + 20;const depth = parseFloat(partDepthSlider.value) * scaleFactor + 20;const moldMaterial = new THREE.MeshStandardMaterial({ color: 0x555555, metalness: 0.8, roughness: 0.5 });const moldGeom = new THREE.BoxGeometry(width, height, depth);moldHalfBottom = new THREE.Mesh(moldGeom, moldMaterial);moldHalfTop = new THREE.Mesh(moldGeom, moldMaterial);// Position halves relative to the part's center plane (y=height/2)const partHeight = parseFloat(partHeightSlider.value) * scaleFactor;moldHalfBottom.position.y = partHeight/2 - height/2; // Center of bottom mold half at part center plane - half mold heightmoldHalfTop.position.y = partHeight/2 + height/2; // Center of top mold half at part center plane + half mold heightscene.add(moldHalfBottom);scene.add(moldHalfTop);partGroup.visible = false; // Hide original part}const targetSeparation = parseFloat(partHeightSlider.value) * scaleFactor * 1.5;const startYTop = moldHalfTop.position.y;const startYBottom = moldHalfBottom.position.y;const targetYTop = startYTop + targetSeparation / 2;const targetYBottom = startYBottom - targetSeparation / 2;const duration = 1000; // mslet startTime = null;function moldStep(timestamp) {if (!startTime) startTime = timestamp;const elapsed = timestamp - startTime;const progress = Math.min(elapsed / duration, 1);const easedProgress = 0.5 - 0.5 * Math.cos(progress * Math.PI);moldHalfTop.position.y = startYTop + (targetYTop - startYTop) * easedProgress;moldHalfBottom.position.y = startYBottom + (targetYBottom - startYBottom) * easedProgress;if (progress < 1) {requestAnimationFrame(moldStep);} else {updateStatus("模拟开模完成。再次点击可复位。");isAnimating = false;simulateMoldButton.disabled = false;resetViewButton.disabled = false;analyzeToleranceButton.disabled = false;isMoldOpen = true;simulateMoldButton.textContent = "复位模具";}}function resetMold() {// Animation to close the moldisAnimating = true;simulateMoldButton.disabled = true;resetViewButton.disabled = true;analyzeToleranceButton.disabled = true;const currentYTop = moldHalfTop.position.y;const currentYBottom = moldHalfBottom.position.y;// Calculate original positions correctly based on part height and mold heightconst partHeight = parseFloat(partHeightSlider.value) * scaleFactor;const moldHeight = parseFloat(partHeightSlider.value) * scaleFactor / 2 + 20;const originalYTop = partHeight / 2 + moldHeight / 2;const originalYBottom = partHeight / 2 - moldHeight / 2;const durationClose = 800;let startTimeClose = null;function closeStep(timestamp) {if (!startTimeClose) startTimeClose = timestamp;const elapsed = timestamp - startTimeClose;const progress = Math.min(elapsed / durationClose, 1);const easedProgress = 0.5 - 0.5 * Math.cos(progress * Math.PI);moldHalfTop.position.y = currentYTop + (originalYTop - currentYTop) * easedProgress;moldHalfBottom.position.y = currentYBottom + (originalYBottom - currentYBottom) * easedProgress;if (progress < 1) {requestAnimationFrame(closeStep);} else {scene.remove(moldHalfTop);scene.remove(moldHalfBottom);moldHalfTop = null;moldHalfBottom = null;partGroup.visible = true; // Show part againupdateStatus("模具已复位。");isAnimating = false;simulateMoldButton.disabled = false;resetViewButton.disabled = false;analyzeToleranceButton.disabled = false;isMoldOpen = false;simulateMoldButton.textContent = "模拟开模 (概念)";}}requestAnimationFrame(closeStep);}if (isMoldOpen) {resetMold();} else {updateStatus("模拟开模过程...");requestAnimationFrame(moldStep);}}function resetCameraView() {controls.reset();// Adjust position based on current part size? Or fixed reset?const currentHeight = parseFloat(partHeightSlider.value) * scaleFactor;camera.position.set(100, 80 + currentHeight/2, 150);camera.lookAt(0, currentHeight / 2, 0); // Look at center of basecontrols.update();updateStatus("视图已重置。");}// --- Utility Functions ---function updateStatus(message) {statusText.textContent = message;console.log("Status:", message);}function onWindowResize() {if (!renderer || !camera) return;const width = rendererContainer.clientWidth;const height = rendererContainer.clientHeight;camera.aspect = width / height;camera.updateProjectionMatrix();renderer.setSize(width, height);}// --- Initialization Call ---try {initThreeJS();setupEventListeners();updateStatus("参数化产品设计组件初始化成功。");} catch (error) {console.error("初始化失败:", error);updateStatus(`错误: ${error.message}`);rendererContainer.innerHTML = `<p style='color: red; padding: 20px;'>无法加载3D视图。错误: ${error.message}</p>`;}
});
相关文章:
11、参数化三维产品设计组件 - /设计与仿真组件/parametric-3d-product-design
76个工业组件库示例汇总 参数化三维产品设计组件 (注塑模具与公差分析) 概述 这是一个交互式的 Web 组件,旨在演示简单的三维零件(如带凸台的方块)的参数化设计过程,并结合注塑模具设计(如开模动画)与公…...
智能座舱开发工程师面试题
一、基础知识类 简述智能座舱的核心组成部分及其功能 要求从硬件(如显示屏、传感器、控制器)和软件(操作系统、中间件、应用程序)层面展开,阐述各部分如何协同实现座舱的智能化体验。 对比 Android Automotive、QNX…...
【连载14】基础智能体的进展与挑战综述-多智能体系统设计
基础智能体的进展与挑战综述 从类脑智能到具备可进化性、协作性和安全性的系统 【翻译团队】刘军(liujunbupt.edu.cn) 钱雨欣玥 冯梓哲 李正博 李冠谕 朱宇晗 张霄天 孙大壮 黄若溪 在基于大语言模型的多智能体系统(LLM-MAS)中,合作目标和合…...
06.three官方示例+编辑器+AI快速学习webgl_animation_skinning_additive_blending
本实例主要讲解内容 这个Three.js示例展示了**骨骼动画(Skinning)和变形动画(Morphing)**的结合应用。通过加载一个机器人模型,演示了如何同时控制角色的肢体动作和面部表情,实现更加丰富的角色动画效果。 核心技术包括: 多动画混合与淡入…...
【Java学习日记36】:javabeen学生系统
ideal快捷键...
.Net HttpClient 使用请求数据
HttpClient 使用请求数据 0、初始化及全局设置 //初始化:必须先执行一次 #!import ./ini.ipynb1、使用url 传参 参数放在Url里,形如:http://www.baidu.com?namezhangsan&age18, GET、Head请求用的比较多。优点是简单、方便࿰…...
详解 Java 并发编程 synchronized 关键字
synchronized 关键字的作用 synchronized 是 Java 中用于实现线程同步的关键字,主要用于解决多线程环境下的资源竞争问题。它可以修饰方法或代码块,确保同一时间只有一个线程可以执行被修饰的代码,从而避免数据不一致的问题。 synchronized…...
《Go小技巧易错点100例》第三十二篇
本期分享: 1.sync.Map的原理和使用方式 2.实现有序的Map sync.Map的原理和使用方式 sync.Map的底层结构是通过读写分离和无锁读设计实现高并发安全: 1)双存储结构: 包含原子化的 read(只读缓存,无锁快…...
时序约束高级进阶使用详解四:Set_False_Path
目录 一、背景 二、Set_False_Path 2.1 Set_false_path常用场景 2.2 Set_false_path的优势 2.3 Set_false_path设置项 2.4 细节区分 三、工程示例 3.1 工程代码 3.2 时序约束如下 3.3 时序报告 3.4 常规场景 3.4.1 设计代码 3.4.2 约束场景 3.4.3 约束对象总结…...
每日定投40刀BTC(16)20250428 - 20250511
定投 坚持 《恒道》 长河九曲本微流,岱岳摩云起累丘。 铁杵十年销作刃,寒窗五鼓淬成钩。已谙蜀栈盘空险,更蓄湘竹带泪遒。 莫问枯荣何日证,星霜满鬓亦从头。...
C# 高效处理海量数据:解决嵌套并行的性能陷阱
C# 高效处理海量数据:解决嵌套并行的性能陷阱 问题场景 假设我们需要在 10万条ID 和 1万个目录路径 中,快速找到所有满足以下条件的路径: 路径本身包含ID字符串该路径的子目录中也包含同名ID 初始代码采用Parallel.ForEach嵌套Task.Run&am…...
【Java EE初阶 --- 多线程(初阶)】线程安全问题
乐观学习,乐观生活,才能不断前进啊!!! 我的主页:optimistic_chen 我的专栏:c语言 ,Java 欢迎大家访问~ 创作不易,大佬们点赞鼓励下吧~ 文章目录 线程不安全的原因根本原因…...
从InfluxDB到StarRocks:Grab实现Spark监控平台10倍性能提升
Grab 是东南亚领先的超级应用,业务涵盖外卖配送、出行服务和数字金融,覆盖东南亚八个国家的 800 多个城市,每天为数百万用户提供一站式服务,包括点餐、购物、寄送包裹、打车、在线支付等。 为了优化 Spark 监控性能,Gr…...
《Redis应用实例》学习笔记,第一章:缓存文本数据
前言 最近在学习《Redis应用实例》,这本书并没有讲任何底层,而是聚焦实战用法,梳理了 32 种 Redis 的常见用法。我的笔记在 Github 上,用 Jupyter 记录,会有更好的阅读体验,作者的源码在这里:h…...
Redis 缓存
缓存介绍 Redis 最主要三个用途: 1)存储数据(内存数据库) 2)消息队列 3)缓存 对于硬件的访问速度,通常有以下情况: CPU 寄存器 > 内存 > 硬盘 > 网络 缓存的核心…...
Apache Flink 与 Flink CDC:概念、联系、区别及版本演进解析
Apache Flink 与 Flink CDC:概念、联系、区别及版本演进解析 在实时数据处理和流式计算领域,Apache Flink 已成为行业标杆。而 Flink CDC(Change Data Capture) 作为其生态中的重要组件,为数据库的实时变更捕获提供了强大的能力。 本文将从以下几个方面进行深入讲解: 什…...
缓存(4):常见缓存 概念、问题、现象 及 预防问题
常见缓存概念 缓存特征: 命中率、最大元素、清空策略 命中率:命中率返回正确结果数/请求缓存次数 它是衡量缓存有效性的重要指标。命中率越高,表明缓存的使用率越高。 最大元素(最大空间):缓存中可以存放的最大元素的…...
实战项目6(09)
目录 任务场景一 【r1配置】 【r2配置】 【r3配置】 任务场景二 【r1配置】 【r2配置】 【r3配置】 任务场景三 【r1配置】 【r2配置】 【r3配置】 任务场景一 按照下图完成网络拓扑搭建和配置 任务要求:在…...
MySQL 数据库故障排查指南
MySQL 数据库故障排查指南 本指南旨在帮助您识别和解决常见的 MySQL 数据库故障。我们将从问题识别开始,逐步深入到具体的故障类型和排查步骤。 1. 问题识别与信息收集 在开始排查之前,首先需要清晰地了解问题的现象和范围。 故障现象: 数…...
MacOS Python3安装
python一般在Mac上会自带,但是大多都是python2。 python2和python3并不存在上下版本兼容的情况,所以python2和python3可以同时安装在一台设备上,并且python3的一些语法和python2并不互通。 所以在Mac电脑上即使有自带python,想要使…...
锁相放大技术:从噪声中提取微弱信号的利器
锁相放大技术:从噪声中提取微弱信号的利器 一、什么是锁相放大? 锁相放大(Lock-in Amplification)是一种用于检测微弱信号的技术,它能够从强噪声背景中提取出我们感兴趣的特定信号。想象一下在嘈杂的派对上听清某个人…...
机器学习总结
1.BN【batch normalization】 https://zhuanlan.zhihu.com/p/93643523 减少 2.L1L2正则化 l1:稀疏 l2:权重减小 3.泛化误差 训练误差计算了训练集的误差,而泛化误差是计算全集的误差。 4.dropout 训练过程中神经元p的概率失活 一文彻底搞懂深度学习&#x…...
基于神经网络的无源雷达测向系统仿真实现
基于神经网络的无源雷达测向系统仿真实现 项目概述 本项目实现了基于卷积神经网络(CNN)的无源雷达方向到达角(DOA)估计系统。通过深度学习方法,系统能够从接收到的雷达信号中准确估计出信号源的方向,适用于单目标和多目标场景。相比传统的DOA估计算法&…...
《用MATLAB玩转游戏开发》Flappy Bird:小鸟飞行大战MATLAB趣味实现
《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互)-Flappy Bird:小鸟飞行大战MATLAB趣味实现 文章目录 《用MATLAB玩转游戏开发:从零开始打造你的数字乐园》基础篇(2D图形交互…...
【C/C++】跟我一起学_C++同步机制效率对比与优化策略
文章目录 C同步机制效率对比与优化策略1 效率对比2 核心同步机制详解与适用场景3 性能优化建议4 场景对比表5 总结 C同步机制效率对比与优化策略 多线程编程中,同步机制的选择直接影响程序性能与资源利用率。 主流同步方式: 互斥锁原子操作读写锁条件变量无锁数据…...
linux 三剑客命令学习
grep Grep 是一个命令行工具,用于在文本文件中搜索打印匹配指定模式的行。它的名称来自于 “Global Regular Expression Print”(全局正则表达式打印),它最初是由 Unix 系统上的一种工具实现的。Grep 工具在 Linux 和其他类 Unix…...
【js基础笔记] - 包含es6 类的使用
文章目录 js基础js 预解析js变量提升 DOM相关知识节点选择器获取属性节点创建节点插入节点替换节点克隆节点获取节点属性获取元素尺寸获取元素偏移量标准的dom事件流阻止事件传播阻止默认行为事件委托 正则表达式js复杂类型元字符 - 基本元字符元字符 - 边界符元字符 - 限定符元…...
《Linux命令行大全(第2版)》PDF下载
内容简介 本书对Linux命令行进行详细的介绍,全书内容包括4个部分,第一部分由Shell的介绍开启命令行基础知识的学习之旅;第二部分讲述配置文件的编辑,如何通过命令行控制计算机;第三部分探讨常见的任务与必备工具&…...
补补表面粗糙度的相关知识(一)
表面粗糙度,或简称粗糙度,是指表面不光滑的特性。这个在机械加工行业内可以说是绝绝的必备知识之一,但往往也是最容易被忽略的,因为往往天天接触的反而不怎么关心,或者没有真正的去认真学习掌握。对于像我一样…...
Python实用工具:pdf转doc
该工具只能使用在英文目录下,且无法转换出图片,以及文本特殊格式。 下载依赖项 pip install PyPDF2 升级依赖项 pip install PyPDF2 --upgrade 查看库版本 python -c "import PyPDF2; print(PyPDF2.__version__)" 下载第二个依赖项 pip i…...
基于Dify实现对Excel的数据分析
在dify部署完成后,大家就可以基于此进行各种应用场景建设,目前dify支持聊天助手(包括对话工作流)、工作流、agent等模式的场景建设,我们在日常工作中经常会遇到各种各样的数据清洗、格式转换处理、数据统计成图等数据分…...
Win全兼容!五五 Excel Word 转 PDF 工具解决多场景转换难题
各位办公小能手们!今天给你们介绍一款超牛的工具——五五Excel Word批量转PDF工具V5.5版。这玩意儿专注搞批量格式转换,能把Excel(.xls/.xlsx)和Word(.doc/.docx)文档唰唰地变成PDF格式。 先说说它的核心功…...
java加强 -Collection集合
集合是一种容器,类似于数组,但集合的大小可变,开发中也非常常用。Collection代表单列集合,每个元素(数据)只包含1个值。Collection集合分为两类,List集合与set集合。 特点 List系列集合&#…...
BGP实验练习1
需求: 要求五台路由器的环回地址均可以相互访问 需求分析: 1.图中存在五个路由器 AR1、AR2、AR3、AR4、AR5,分属不同自治系统(AS),AR1 在 AS 100,AR2 - AR4 在 AS 200,AR5 在 AS …...
Nginx location静态文件映射配置
遇到问题? 以下这个Nginx的配置,愿意为访问https://abc.com会指向一个动态网站,访问https://abc.com/tongsongzj时会访问静态网站,但是配置之后(注意看后面那个location /tongsongzj/静态文件映射的配置)&…...
四、Hive DDL表定义、数据类型、SerDe 与分隔符核心
在理解了 Hive 数据库的基本操作后,本篇笔记将深入到数据存储的核心单元——表 (Table) 的定义和管理。掌握如何创建表、选择合适的数据类型、以及配置数据的读写方式 (特别是 SerDe 和分隔符),是高效使用 Hive 的关键。 一、创建表 (CREATE TABLE)&…...
每日脚本 5.11 - 进制转换和ascii字符
前置知识 python中各个进制的开头 二进制 : 0b 八进制 : 0o 十六进制 : 0x 进制转换函数 : bin() 转为2进制 oct() 转换为八进制的函数 hex() 转换为16进制的函数 ascii码和字符之间的转换 : chr(97) 码转为字符 …...
cookie和session的区别
一、基本概念 1. Cookie 定义:Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据(通常小于4KB),浏览器会在后续请求中自动携带该数据。作用:用于跟踪用户状态(如登录状态)、记…...
Kotlin Multiplatform--03:项目实战
Kotlin Multiplatform--03:项目实战 引言配置iOS开发环境配置项目环境运行程序 引言 本章将会带领读者进行项目实战,了解如何从零开始编译一个能同时在Android和iOS运行的App。开发环境一般来说需要使用Macbook,笔者没试过Windows是否能开发。…...
图形学、人机交互、VR/AR领域文献速读【持续更新中...】
(1)笔者在时间有限的情况下,想要多积累一些自身课题之外的新文献、新知识,所以开了这一篇文章。 (2)想通过将文献喂给大模型,并向大模型提问的方式来快速理解文献的重要信息(如基础i…...
opencascade.js stp vite 调试笔记
Hello, World! | OpenCascade.js cnpm install opencascade.js cnpm install vite-plugin-wasm --save-dev 当你不知道文件写哪的时候trae还是有点用的 ‘’‘ import { defineConfig } from vite; import wasm from vite-plugin-wasm; import rollupWasm from rollup/plugi…...
openharmony系统移植之gpu mesa3d适配
openharmony系统移植之gpu mesa3d适配 文章目录 openharmony系统移植之gpu mesa3d适配1. 环境说明2. gpu内核panfrost驱动2.1 使能panfrost驱动2.2 panfrost dts配置 3. buildroot下测试gpu驱动3.1 buildroot配置编译 4. ohos下mesa3d适配4.1 ohos下mesa3d编译调试4.1.2 编译4.…...
Java开发经验——阿里巴巴编码规范经验总结2
摘要 这篇文章是关于Java开发中阿里巴巴编码规范的经验总结。它强调了避免使用Apache BeanUtils进行属性复制,因为它效率低下且类型转换不安全。推荐使用Spring BeanUtils、Hutool BeanUtil、MapStruct或手动赋值等替代方案。文章还指出不应在视图模板中加入复杂逻…...
Linux中常见开发工具简单介绍
目录 apt/yum 介绍 常用命令 install remove list vim 介绍 常用模式 命令模式 插入模式 批量操作 底行模式 模式替换图 vim的配置文件 gcc/g 介绍 处理过程 预处理 编译 汇编 链接 库 静态库 动态库(共享库) make/Makefile …...
深入理解深度Q网络DQN:基于python从零实现
DQN是什么玩意儿? 深度Q网络(DQN)是深度强化学习领域里一个超厉害的算法。它把Q学习和深度神经网络巧妙地结合在了一起,专门用来搞定那些状态空间维度特别高、特别复杂的难题。它展示了用函数近似来学习价值函数的超能力…...
使用lldb看看Rust的HashMap
目录 前言 正文 读取桶的状态 获取键值对 键值对的指针地址 此时,读取数据 读取索引4的键值对 多添加几个键值对 使用i32作为键,&str作为值 使用i32作为键,String作为值 前言 前面使用ldb看了看不同的类型,这篇再使用…...
Vue3简易版购物车的实现。
文章目录 一、话不多说,直接上代码? 一、话不多说,直接上代码? <template><div><input type"text" placeholder"请输入内容" v-model"keywords"><button click"addGood…...
比亚迪全栈自研生态的底层逻辑
比亚迪全栈自研生态的底层逻辑:汽车工程师必须理解的闭环技术革命 引言:当技术壁垒成为护城河 2023年比亚迪销量突破302万辆的震撼数据背后,隐藏着一个更值得工程师深思的事实:其全栈自研体系覆盖了新能源汽车83%的核心零部件。这…...
[Java实战]Spring Boot 快速配置 HTTPS 并实现 HTTP 自动跳转(八)
[Java实战]Spring Boot 快速配置 HTTPS 并实现 HTTP 自动跳转(八) 引言 在当今网络安全威胁日益严峻的背景下,为 Web 应用启用 HTTPS 已成为基本要求。Spring Boot 提供了简单高效的方式集成 HTTPS 支持,无论是开发环境测试还是生产环境部署࿰…...
5.1.1 WPF中Command使用介绍
WPF 的命令系统是一种强大的输入处理机制,它比传统的事件处理更加灵活和可重用,特别适合 MVVM (Model, View, ViewModel)模式开发。 一、命令系统核心概念 1.命令系统基本元素: 命令(Command): 即ICommand类,使用最多的是RoutedCommand,也可以自己继承ICommand使用自定…...