/* 线束与走线指示系统(通道管、扎带、T 接、缓冲环、桥接曲线) */ /* ===================== 线束与走线指示系统 (Wire Harness System) ===================== */ const harnessGroup = new THREE.Group(); harnessGroup.name = 'HARNESS_ROOT'; scene.add(harnessGroup); const matTrunk = new THREE.MeshStandardMaterial({ color: 0xf59e0b, roughness: 0.35, metalness: 0.25 }); const matDrop = new THREE.MeshStandardMaterial({ color: 0x10b981, roughness: 0.3, metalness: 0.2 }); const matTap = new THREE.MeshStandardMaterial({ color: 0x8b5cf6, roughness: 0.4, metalness: 0.3 }); const matTerm = new THREE.MeshStandardMaterial({ color: 0xef4444, roughness: 0.2, metalness: 0.5 }); const matClip = new THREE.MeshStandardMaterial({ color: 0x1e293b, roughness: 0.5, metalness: 0.2 }); const harnessMeshes = []; function makeHarnTube(ptsCAD, r, mat, name, cn, parentGrp, segs = 18, radSegs = 8) { const pts = ptsCAD.map(p => new THREE.Vector3(...VC(p))); const curve = new THREE.CatmullRomCurve3(pts); const geo = new THREE.TubeGeometry(curve, segs, r, radSegs, false); const mesh = new THREE.Mesh(geo, mat); mesh.userData = { idx: 'H-CABLE', name: name || '4芯高柔拖链总线 (CAN+24V)', cn: cn || '主干总线', vol: 8000, printed: false, harness: true }; (parentGrp || harnessGroup).add(mesh); harnessMeshes.push(mesh); return mesh; } function makeHarnTap(pCAD, label, parentGrp) { const geo = new THREE.BoxGeometry(10, 8, 14); const mesh = new THREE.Mesh(geo, matTap); mesh.position.set(...VC(pCAD)); mesh.userData = { idx: 'H-TAP', name: label || 'T接分线小板', cn: '微型T型总线分线节点', vol: 1100, printed: false, harness: true }; const ledGeo = new THREE.SphereGeometry(1.2, 8, 8); const ledMat = new THREE.MeshBasicMaterial({ color: 0x34d399 }); const led = new THREE.Mesh(ledGeo, ledMat); led.position.set(0, 4.5, 0); mesh.add(led); (parentGrp || harnessGroup).add(mesh); harnessMeshes.push(mesh); return mesh; } function makeHarnClip(pCAD, parentGrp) { const geo = new THREE.CylinderGeometry(3.0, 3.0, 4.0, 12, 1, true); const mesh = new THREE.Mesh(geo, matClip); mesh.position.set(...VC(pCAD)); (parentGrp || harnessGroup).add(mesh); harnessMeshes.push(mesh); return mesh; } function makeHarnTerm(pCAD, parentGrp) { const geo = new THREE.CylinderGeometry(2.5, 2.5, 8.0, 12); const mesh = new THREE.Mesh(geo, matTerm); mesh.position.set(...VC(pCAD)); mesh.rotation.z = Math.PI / 2; mesh.userData = { idx: 'H-TERM', name: '120Ω 终端电阻 (总线末端阻抗匹配)', cn: 'CAN总线终端匹配电阻', vol: 500, printed: false, harness: true }; (parentGrp || harnessGroup).add(mesh); harnessMeshes.push(mesh); return mesh; } // 1. Root group harness (P01 立柱、P03 吊臂、M1 电机) const hRoot = new THREE.Group(); hRoot.name = 'HARN_ROOT_SUB'; JG['root'].add(hRoot); makeHarnTube([ [-115.0, 128.0, 35.0], [-115.0, 128.0, 250.0], [-115.0, 128.0, 470.0], [-115.0, 128.0, 500.0] ], 2.0, matTrunk, 'P01立柱内穿线管主干 (24V+CAN,穿44x44内腔与P03减重窗)', '基座立柱主干', hRoot); makeHarnTube([ [-115.0, 128.0, 500.0], [-115.0, 101.5, 520.0], [-105.0, 90.0, 545.0], [-85.0, 89.8, 570.0], [-70.0, 87.4, 592.0], [-56.0, 85.6, 610.0], [-20.0, 85.8, 610.0], [5.5, 86.1, 610.0] ], 1.8, matTrunk, 'P03吊臂侧向骨架主干 (扎带拱桥锁附,线心贴桥外露面)', 'P03吊臂实体主干', hRoot, 24, 8); makeHarnTap([-115.0, 128.0, 505.0], 'T接小板① (M1供电分线节点)', hRoot); // M1 外部大空间绕行通道:线束自基座 +Y 侧向外绕过 P03 法兰外廓(免打孔),在外部宽阔自由空间大半径弧形引入 M1 电机侧向缺口 makeHarnTube([ [-115.0, 128.0, 505.0], [-115.0, 155.0, 515.0], [-115.0, 172.0, 530.0], [-95.0, 172.0, 565.0], [-75.0, 172.0, 595.0], [-55.0, 172.0, 610.15], [-46.0, 166.0, 610.15], [-48.5, 158.0, 610.15], [-49.5, 154.5, 610.15] ], 1.5, matDrop, 'P03吊臂M1供电支干 (外侧大空间绕行引入)', 'M1 供电支干', hRoot, 16, 8); makeHarnTap([-46.0, 166.0, 610.15], 'M1大空间侧向接插件 (20x20mm容纳位)', hRoot); makeHarnClip([-115.0, 128.0, 300.0], hRoot); makeHarnClip([-85.0, 89.8, 570.0], hRoot); makeHarnClip([-56.0, 85.6, 610.0], hRoot); makeHarnClip([5.5, 86.1, 610.0], hRoot); const anchorA0 = new THREE.Object3D(); anchorA0.position.set(...VC([5.5, 86.1, 610.0])); hRoot.add(anchorA0); // 2. J1 group harness (P04 肩叉、M2 电机) const hJ1 = new THREE.Group(); hJ1.name = 'HARN_J1'; JG['J1'].add(hJ1); const anchorB1 = new THREE.Object3D(); anchorB1.position.set(...VC([20.0, 87.0, 615.0])); hJ1.add(anchorB1); makeHarnTube([ [20.0, 87.0, 615.0], [20.0, 87.0, 585.0], [20.0, 87.0, 560.0], [20.0, 87.0, 535.0], [20.0, 87.0, 515.0], [5.0, 87.0, 500.0], [-10.0, 87.0, 492.0], [-20.0, 88.0, 490.0], [-22.0, 96.0, 490.0], [-20.0, 102.5, 490.0], [-17.5, 102.5, 485.0] ], 1.8, matTrunk, 'P04肩叉侧向外廓线卡走线 (扎带拱桥锁附)', 'P04 侧向走线', hJ1, 24, 8); makeHarnTap([-17.5, 102.5, 490.0], 'T接小板② (J2/M2 分线节点)', hJ1); // M2 侧向缺口出线 (CAD Notch: [-8.04, 102.50, 487.28],电机后端盖圆形PCBA侧向出线槽) makeHarnTube([ [-8.04, 102.50, 487.28], [-13.0, 102.50, 488.5], [-17.5, 102.50, 490.0] ], 1.3, matDrop, 'M2 电机侧向缺口引出线 (2xCAN + 2x电源)', 'M2 出线分支', hJ1, 10, 6); makeHarnClip([20.0, 87.0, 615.0], hJ1); makeHarnClip([20.0, 87.0, 560.0], hJ1); makeHarnClip([20.0, 87.0, 515.0], hJ1); const anchorA1 = new THREE.Object3D(); anchorA1.position.set(...VC([-17.5, 102.5, 485.0])); hJ1.add(anchorA1); // 3. J2 group harness (P05 大臂旋转座、M3 电机) const hJ2 = new THREE.Group(); hJ2.name = 'HARN_J2'; JG['J2'].add(hJ2); const anchorB2 = new THREE.Object3D(); anchorB2.position.set(...VC([-17.5, 102.5, 445.0])); hJ2.add(anchorB2); makeHarnTube([ [-17.5, 102.5, 445.0], [-17.5, 102.5, 440.0] ], 1.8, matTrunk, 'P05大臂旋转座侧向外廓走线 (外侧平滑过渡,不穿电机)', 'P05 侧向走线', hJ2, 6, 6); makeHarnTap([-17.5, 102.5, 440.0], 'T接小板③ (J3/M3 分线节点)', hJ2); // M3 侧向缺口出线 (CAD Notch: [22.09, 100.30, 442.50],前向敞开侧圆形PCBA径向缺口) makeHarnTube([ [22.09, 100.30, 442.50], [22.09, 93.0, 443.0], [5.0, 94.0, 444.0], [-10.0, 98.0, 445.0], [-17.5, 102.50, 440.0] ], 1.3, matDrop, 'M3 电机侧向缺口引出线 (2xCAN + 2x电源)', 'M3 出线分支', hJ2, 12, 6); makeHarnClip([-17.5, 102.5, 425.0], hJ2); const anchorA2 = new THREE.Object3D(); anchorA2.position.set(...VC([-17.5, 102.5, 400.0])); hJ2.add(anchorA2); makeHarnTube([ [-17.5, 102.5, 440.0], [-17.5, 102.5, 425.0], [-17.5, 102.5, 400.0] ], 1.8, matTrunk, 'P05大臂旋转座侧向外壁引出段', 'P05引出', hJ2, 8, 6); // 4. J3 group harness (P06 大臂骨架、M4 电机) const hJ3 = new THREE.Group(); hJ3.name = 'HARN_J3'; JG['J3'].add(hJ3); const anchorB3 = new THREE.Object3D(); anchorB3.position.set(...VC([-17.5, 102.5, 360.0])); hJ3.add(anchorB3); makeHarnTube([ [-17.5, 102.5, 360.0], [-20.7, 102.5, 345.0] ], 1.8, matTrunk, 'P06大臂外侧线槽走线', 'P06 侧向走线', hJ3, 8, 6); makeHarnTap([-17.5, 102.5, 332.0], 'T接小板④ (J4/M4 肘部分线节点)', hJ3); // M4 肘电机侧向缺口出线 (CAD Notch: [-8.04, 102.50, 329.28],电机后端盖圆形PCBA侧向出线槽) makeHarnTube([ [-8.04, 102.50, 329.28], [-13.0, 102.50, 330.5], [-17.5, 102.50, 332.0] ], 1.3, matDrop, 'M4 肘电机侧向引出线 (2xCAN + 2x电源)', 'M4 出线分支', hJ3, 10, 6); makeHarnClip([-20.7, 102.5, 345.0], hJ3); makeHarnTube([ [-20.7, 102.5, 345.0], [-17.5, 102.5, 320.0], [-17.5, 102.5, 305.0] ], 1.8, matTrunk, 'P06大臂侧向外廊引出段', 'P06引出', hJ3, 8, 6); const anchorA3 = new THREE.Object3D(); anchorA3.position.set(...VC([-17.5, 102.5, 305.0])); hJ3.add(anchorA3); // 5. J4 group harness (P07 小臂骨架、M5 电机) const hJ4 = new THREE.Group(); hJ4.name = 'HARN_J4'; JG['J4'].add(hJ4); const anchorB4 = new THREE.Object3D(); anchorB4.position.set(...VC([-17.5, 102.5, 255.0])); hJ4.add(anchorB4); makeHarnTube([ [-17.5, 102.5, 255.0], [-20.7, 102.5, 235.0] ], 1.8, matTrunk, 'P07小臂侧向外廓线槽走线 (外侧平滑过渡,不强行内穿)', 'P07 侧向走线', hJ4, 10, 6); makeHarnTap([-17.5, 102.5, 222.0], 'T接小板⑤ (J5/M5 分线节点)', hJ4); // M5 腕仰俯电机侧向缺口出线 (CAD Notch: [-8.04, 102.50, 219.28],电机后端盖圆形PCBA侧向出线槽) makeHarnTube([ [-8.04, 102.50, 219.28], [-13.0, 102.50, 220.5], [-17.5, 102.50, 222.0] ], 1.3, matDrop, 'M5 电机侧向引出线 (2xCAN + 2x电源)', 'M5 出线分支', hJ4, 10, 6); makeHarnClip([-20.7, 102.5, 235.0], hJ4); makeHarnTube([ [-20.7, 102.5, 235.0], [-17.5, 102.5, 215.0], [-17.5, 102.5, 205.0] ], 1.8, matTrunk, 'P07小臂侧向外壁引出段', 'P07引出', hJ4, 8, 6); const anchorA4 = new THREE.Object3D(); anchorA4.position.set(...VC([-17.5, 102.5, 205.0])); hJ4.add(anchorA4); // 6. J5 group harness (P08 腕偏转座、M6 电机) const hJ5 = new THREE.Group(); hJ5.name = 'HARN_J5'; JG['J5'].add(hJ5); const anchorB5 = new THREE.Object3D(); anchorB5.position.set(...VC([-17.5, 102.5, 175.0])); hJ5.add(anchorB5); makeHarnTube([ [-17.5, 102.5, 175.0], [-17.5, 102.5, 172.0] ], 1.8, matTrunk, 'P08腕偏转座侧向走线', 'P08 腕部外侧走线', hJ5, 6, 6); makeHarnTap([-17.5, 102.5, 172.0], 'T接小板⑥ (J6/M6 终端分线节点)', hJ5); // M6 腕自转电机侧向缺口出线 (CAD Notch: [22.09, 100.30, 173.50],前向敞开侧圆形PCBA径向缺口) makeHarnTube([ [22.09, 100.30, 173.50], [22.09, 93.0, 174.0], [5.0, 94.0, 174.5], [-10.0, 98.0, 175.0], [-17.5, 102.50, 172.0] ], 1.3, matDrop, 'M6 腕电机侧向引出线 (2xCAN + 2x电源)', 'M6 出线分支', hJ5, 12, 6); makeHarnTerm([-17.5, 102.5, 168.0], hJ5); // 7. Dynamic Flexible Bridges across J1~J5 (Service Loop / 弓形弹性缓冲回环) const bridgeMeshes = []; for (let i = 0; i < 5; i++) { const geo = new THREE.BufferGeometry(); const mesh = new THREE.Mesh(geo, matTrunk); mesh.userData = { idx: 'H-BRIDGE-' + (i + 1), name: 'J' + (i + 1) + ' 跨关节柔性服务回环 (Service Loop / 弓形缓冲弯曲段)', cn: 'J' + (i + 1) + ' 柔性缓冲回环', vol: 2500, printed: false, harness: true }; harnessGroup.add(mesh); bridgeMeshes.push(mesh); harnessMeshes.push(mesh); } const allHarnessContainers = [hRoot, hJ1, hJ2, hJ3, hJ4, hJ5, harnessGroup]; let harnessVisibleState = true; function setHarnessVisible(v) { harnessVisibleState = v; allHarnessContainers.forEach(g => g.visible = v); if (v) updateHarnessBridges(); requestRender(); } window.setHarnessVisible = setHarnessVisible; window.isHarnessVisible = () => harnessVisibleState; const _pA = new THREE.Vector3(), _pB = new THREE.Vector3(); function updateHarnessBridges() { if (!harnessVisibleState) return; scene.updateMatrixWorld(); const pairs = [ { a: anchorA0, b: anchorB1, type: 'roll1', slackL: 25, minH: 5, dir: new THREE.Vector3(0, 0.1, 0.99) }, // J1 roll { a: anchorA1, b: anchorB2, type: 'pitch2', slackL: 48, minH: 8, dir: new THREE.Vector3(-1, 0, 0) }, // J2 pitch { a: anchorA2, b: anchorB3, type: 'roll3', slackL: 46, minH: 7, dir: new THREE.Vector3(-0.7, 0, 0.7) }, // J3 roll { a: anchorA3, b: anchorB4, type: 'pitch4', slackL: 56, minH: 8, dir: new THREE.Vector3(-1, 0, 0) }, // J4 elbow pitch { a: anchorA4, b: anchorB5, type: 'pitch5', slackL: 36, minH: 6, dir: new THREE.Vector3(-1, 0, 0) }, // J5 wrist pitch ]; for (let i = 0; i < 5; i++) { const pr = pairs[i]; pr.a.getWorldPosition(_pA); pr.b.getWorldPosition(_pB); const m = bridgeMeshes[i]; const dist = _pA.distanceTo(_pB); const halfL = Math.max(dist * 0.5 + 2, pr.slackL * 0.5); const halfD = dist * 0.5; const h = Math.sqrt(Math.max(pr.minH * pr.minH, halfL * halfL - halfD * halfD)); const dirNorm = pr.dir.clone().normalize(); // 13 点正弦弹性拱形样条插值:端点切向连续,中间大半径圆滑过渡,彻底杜绝折角与多边形棱角 const pts = []; const N = 12; for (let k = 0; k <= N; k++) { const u = k / N; const base = new THREE.Vector3().lerpVectors(_pA, _pB, u); const bulge = Math.sin(Math.PI * u) * h; base.addScaledVector(dirNorm, bulge); pts.push(base); } const curve = new THREE.CatmullRomCurve3(pts, false, 'centripetal', 0.5); if (m.geometry) m.geometry.dispose(); m.geometry = new THREE.TubeGeometry(curve, 32, 1.8, 10, false); } } window.updateHarnessBridges = updateHarnessBridges; window.harnessGroup = harnessGroup; window.harnessMeshes = harnessMeshes; updateHarnessBridges(); const J6j = JO.find(j => j.id === 'J6'); // the jaws slide along the gripper motor's own local X, so that axis must come from J6 const JAWD = J6j ? new V3(...VC(J6j.xdir)).normalize() : new V3(0, 0, 1); const PIN_R = 14.0; const JMAX = TL.J6 ? PIN_R * (1 - Math.cos(TL.J6.hi * Math.PI / 180)) : 9.0; const JAW_MAX = Math.min(9.0, Math.max(2.0, JMAX)); let jawMM = 0; function updXform() { for (const m of meshes) { m.position.copy(m.base).multiplyScalar(expl); if (m.userData.jaw) m.position.addScaledVector(JAWD, m.userData.jaw * jawMM); } if (window.setHarnessVisible) { const bH = document.getElementById('bHarn'); const on = bH ? bH.classList.contains('on') : true; setHarnessVisible(on && (expl < 0.05)); } requestRender(); } function setJaw(t) { jawMM = Math.max(0, Math.min(JAW_MAX, t)); // the crank pins sit at radius 14 along the jaw axis as authored, so a rotation of phi // puts the pin at x = 14*cos(phi) and each jaw rides out by exactly that much const phi = Math.acos(Math.max(-1, Math.min(1, (PIN_R - jawMM) / PIN_R))); ang.J6 = -phi * 180 / Math.PI; applyJoint('J6'); updXform(); const e = document.getElementById('vJ6'); if (e) e.textContent = (2 * jawMM).toFixed(1) + ' mm'; } function setExpl(k) { expl = k; updXform(); if (typeof updStat === 'function') updStat(); requestRender(); } setExpl(0); // start assembled, not exploded