state / harness / camera / pick / ui / sheets / render / urlparams。 经典脚本的顶层 const/let 落在同一个全局词法作用域,所以切片本身不改变任何行为—— 这一步先让"文件边界"与"模块语义"两件事分离:翻 ESM 时若出问题,责任清楚在 import/export,而不是切片切错了行。 切片踩到并修掉的:urlparams.js 被切掉了 IIFE 的收尾 })();(我按 len-2 收边界), node --check 当场报 Unexpected end of input。 截图基线分级也在这一步收敛:工程图/装配向导视图带 feGaussianBlur 发光滤镜, 同一构建两轮就有 ±1 抖动(先前扫描那两轮碰巧逐字节相同,才漏进"稳定"清单, 于是随机假 FAIL)。把 8 张工程图/向导视图连同 3 张线束 x-ray、boss_p07、 p03_mount_loose 一起转像素容差(存参考像素,阈值 mean<=0.02、单像素<=32, 约实测 20x 余量),其余 92 张仍要求逐字节相同。 关卡:92 张逐字节一致 + 13 张在噪声阈内;数据哈希与 IK exact 段一致。
369 lines
14 KiB
JavaScript
369 lines
14 KiB
JavaScript
/* 线束与走线指示系统(通道管、扎带、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
|
|
|