feat: P07小臂骨架顶板整条加高19mm补强+前后外缘R3圆角,gusset弧R28->R14随顶板联动相切融入;重算全关节行程J4反向-121->-111,同步重建模型数据与查看器
This commit is contained in:
+82
-43
@@ -9,9 +9,9 @@
|
||||
html,body{margin:0;height:100%;background:var(--bg);color:var(--tx);
|
||||
font:13px/1.55 "Microsoft YaHei","Segoe UI",system-ui,sans-serif;overflow:hidden;
|
||||
-webkit-user-select:none;user-select:none}
|
||||
#app{display:grid;grid-template-columns:274px minmax(430px,1fr) 320px;grid-template-rows:46px 1fr 26px;
|
||||
#app{display:grid;grid-template-columns:274px minmax(430px,1fr);grid-template-rows:46px 1fr 26px;
|
||||
height:100%;min-width:0;overflow-x:auto}
|
||||
header{grid-column:1/4;display:flex;align-items:center;gap:8px;padding:0 12px;flex-wrap:nowrap;
|
||||
header{grid-column:1/-1;display:flex;align-items:center;gap:8px;padding:0 12px;flex-wrap:nowrap;
|
||||
overflow-x:auto;overflow-y:hidden;background:var(--pan);border-bottom:1px solid var(--line)}
|
||||
header .q{white-space:nowrap;flex:none;color:var(--dim);font-size:12px}
|
||||
header b{font-size:14px;white-space:nowrap;flex:none}
|
||||
@@ -22,19 +22,15 @@ button:hover{border-color:var(--ac);color:#fff}
|
||||
button.on{background:#22406b;border-color:var(--ac)}
|
||||
aside{background:var(--pan);overflow:auto;padding:8px;min-width:0}
|
||||
#left{border-right:1px solid var(--line)}
|
||||
#right{border-left:1px solid var(--line)}
|
||||
#app.r{grid-template-columns:274px minmax(430px,1fr) 320px}
|
||||
#app.l{grid-template-columns:274px minmax(430px,1fr)}
|
||||
#stage{position:relative;overflow:hidden;min-width:0;
|
||||
background:radial-gradient(120% 120% at 50% 0%,#1b2028 0%,#0d0f13 70%)}
|
||||
canvas{display:block;width:100%;height:100%}
|
||||
footer{grid-column:1/4;background:var(--pan);border-top:1px solid var(--line);
|
||||
footer{grid-column:1/-1;background:var(--pan);border-top:1px solid var(--line);
|
||||
display:flex;align-items:center;gap:14px;padding:0 12px;color:var(--dim);font-size:12px;
|
||||
white-space:nowrap;overflow:hidden}
|
||||
@media (max-width:1150px){
|
||||
#app{grid-template-columns:250px minmax(400px,1fr)}
|
||||
#right{display:none}
|
||||
#app.r #right{display:block}
|
||||
}
|
||||
@media (max-width:760px){
|
||||
#app{grid-template-columns:minmax(0,1fr)}
|
||||
@@ -75,6 +71,27 @@ label em.ok{color:#5ddc90}
|
||||
svg{display:block;width:100%;height:auto;background:#fff;border-radius:6px}
|
||||
#sheet{display:none;padding:10px;overflow:auto;height:100%}
|
||||
small{color:var(--dim)}
|
||||
/* ---- edge BOM dock (collapsible drawer at the right edge) ---- */
|
||||
#bomTab{position:fixed;right:0;top:64px;z-index:30;writing-mode:vertical-rl;text-orientation:mixed;
|
||||
background:var(--pan2);color:var(--tx);border:1px solid var(--line);border-right:none;
|
||||
border-radius:6px 0 0 6px;padding:12px 6px;font:inherit;cursor:pointer;letter-spacing:3px}
|
||||
#bomTab:hover{border-color:var(--ac);color:#fff}
|
||||
#bomTab b{color:var(--ac)}
|
||||
#bomDock{position:fixed;right:0;top:46px;bottom:26px;width:min(430px,94vw);z-index:29;
|
||||
transform:translateX(103%);transition:transform .22s ease;display:flex;flex-direction:column;
|
||||
background:var(--pan);border-left:1px solid var(--ac);box-shadow:-10px 0 28px rgba(0,0,0,.5)}
|
||||
#bomDock.open{transform:none}
|
||||
#bomDock .dk-hd{display:flex;align-items:center;gap:8px;padding:9px 10px;flex:none;
|
||||
border-bottom:1px solid var(--line)}
|
||||
#bomDock .dk-hd b{font-size:13px}
|
||||
#bomDock .dk-hd .q{color:var(--dim);font-size:11px}
|
||||
#bomDock .dk-x{margin-left:auto}
|
||||
#bomDock .dk-bd{overflow:auto;padding:8px 11px 16px;flex:1;min-height:0}
|
||||
.dk-sec{margin:4px 0 5px;color:var(--ac);font-size:12px;letter-spacing:.4px}
|
||||
.dk-note{color:var(--dim);font-size:11px;margin:1px 0 6px}
|
||||
#screws td{white-space:normal}
|
||||
#screws td:first-child{white-space:nowrap}
|
||||
@media (max-width:760px){#bomTab{top:auto;bottom:36px}#bomDock{top:46px;bottom:26px}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -97,7 +114,6 @@ small{color:var(--dim)}
|
||||
<button id="bXRay" class="on">电机半透</button>
|
||||
<button id="bFit">复位</button>
|
||||
<button id="bL" title="显示/隐藏左栏">左栏</button>
|
||||
<button id="bR" title="显示/隐藏右栏">右栏</button>
|
||||
</header>
|
||||
|
||||
<aside id="left">
|
||||
@@ -121,15 +137,6 @@ small{color:var(--dim)}
|
||||
<div id="sheet"></div>
|
||||
</div>
|
||||
|
||||
<aside id="right">
|
||||
<h3>BOM / 明细</h3>
|
||||
<table id="bom"><thead><tr><th>#</th><th>件号</th><th>cm3</th><th>g</th></tr></thead><tbody></tbody></table>
|
||||
<h3>接口自检(实测值)</h3>
|
||||
<div class="kv" id="iface"></div>
|
||||
<h3>碰撞检查</h3>
|
||||
<div id="col"></div>
|
||||
</aside>
|
||||
|
||||
<footer>
|
||||
<span>左键旋转 · 滚轮缩放 · 右键平移(拖动) · 单击选中高亮 · <b>右键单击隐藏部件(或按H键)</b> · 左侧全选/勾选恢复</span>
|
||||
<span class="sp"></span>
|
||||
@@ -137,6 +144,21 @@ small{color:var(--dim)}
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<button id="bomTab" title="展开 / 收起 物料清单 BOM">物料清单 <b>BOM</b></button>
|
||||
<aside id="bomDock">
|
||||
<div class="dk-hd">
|
||||
<b>物料清单 BOM</b><span class="q" id="bomStat"></span>
|
||||
<button id="bomHide" class="dk-x" title="收起">收起 ▶</button>
|
||||
</div>
|
||||
<div class="dk-bd">
|
||||
<div class="dk-sec">紧固件 / 螺丝清单(全部紧固点)</div>
|
||||
<div class="dk-note" id="scrSum"></div>
|
||||
<table id="screws"><thead><tr><th>规格</th><th>长 mm</th><th>数量</th><th>位置 / 用途</th></tr></thead><tbody></tbody></table>
|
||||
<div class="dk-sec">打印件 BOM(原明细,PLA)</div>
|
||||
<table id="bom"><thead><tr><th>#</th><th>件号</th><th>cm3</th><th>g</th></tr></thead><tbody></tbody></table>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<script>/*__THREE__*/</script>
|
||||
<script>
|
||||
const DATA = /*__DATA__*/;
|
||||
@@ -249,6 +271,10 @@ setExpl(0); // start assembled, not exploded
|
||||
|
||||
/* ---- camera orbit ---- */
|
||||
let az = -0.9, po = 1.15, rd = 620, tx = 120, ty = 190, tz = -30;
|
||||
// lookAt with the world up-axis degenerate-rolls the image when looking near-vertically,
|
||||
// so top view pins the screen-up to -Z: model X runs right, depth runs down-screen
|
||||
const UP_Y = new V3(0, 1, 0), UP_TOP = new V3(0, 0, -1);
|
||||
let camUp = UP_Y.clone();
|
||||
function fitTo(b) {
|
||||
const c = [(b[0][0]+b[1][0])/2, (b[0][1]+b[1][1])/2, (b[0][2]+b[1][2])/2];
|
||||
tx = c[0]; ty = c[1]; tz = c[2];
|
||||
@@ -268,6 +294,7 @@ let BB = [[1e9,1e9,1e9],[-1e9,-1e9,-1e9]];
|
||||
})();
|
||||
fitTo(BB);
|
||||
function updCam() {
|
||||
cam.up.copy(camUp);
|
||||
cam.position.set(tx + rd*Math.sin(po)*Math.cos(az), ty + rd*Math.cos(po), tz + rd*Math.sin(po)*Math.sin(az));
|
||||
cam.lookAt(tx, ty, tz);
|
||||
}
|
||||
@@ -496,30 +523,40 @@ document.getElementById('sum').innerHTML =
|
||||
'<i>总质量(PLA)</i><span>' + gTot.toFixed(0) + ' g</span>' +
|
||||
'<i>电机数量</i><span>' + NMOT + ' 台(同款 电机.STEP)</span>' +
|
||||
'<i>工作空间</i><span>约 ' + Math.round(BB[1][2]) + ' x ' + Math.round(BB[1][0]) + ' mm</span>';
|
||||
/* ---- measured interfaces ---- */
|
||||
const md = DATA.measured, Mt = md.M, Ct = md.C;
|
||||
document.getElementById('iface').innerHTML = [
|
||||
['电机外形', 'Ø' + Mt.hous_d + ' x ' + Mt.hous_len + ',整机长 ' + Mt.cover_z + ',尾部 PCBA 方 ' + Mt.pcba_w],
|
||||
['减速', '两级 0.5M 行星:24T 太阳 / 3x36T 行星 / 96T 内齿'],
|
||||
['输出接口', 'Ø' + Mt.neck_d + 'x' + Mt.neck_depth + ' 颈孔 + Ø' + Mt.centre_d + ' 中心孔'],
|
||||
['螺栓', '3 x M2 @ BCD ' + Mt.m2_bcd + ',时钟 ' + Mt.m2_clock.join('/')],
|
||||
['贴合面', '电机尾基准 + ' + md.HUB_FACE_Z + ' = 从动盘贴合面(联轴器两盖板面跨距 ' + Ct.gap + ')'],
|
||||
['抱箍', '带宽 ' + Ct.clamp_od_r*2 + ',凸耳 R' + Ct.lug_r1 + ',轴向 ' + md.CLAMP_SPAN.join('~')],
|
||||
['齿', Ct.n_teeth + ' 齿端面花键 R' + Ct.tooth_r0 + '~' + Ct.tooth_r1 + ',齿高 ' + Ct.tooth_s],
|
||||
].map(r => '<i>' + r[0] + '</i><span>' + r[1] + '</span>').join('');
|
||||
/* ---- collision ---- */
|
||||
const CL = /*__COL__*/;
|
||||
document.getElementById('col').innerHTML = CL.length
|
||||
? '<table><tr><th>零件对</th><th>mm3</th></tr>' +
|
||||
CL.map(r => '<tr><td>' + r[0] + ' x ' + r[1] + '</td><td class="bad">' + r[2] + '</td></tr>').join('') + '</table>'
|
||||
: '<span class="good">0 对干涉(全部零件布尔交集体积 < 2 mm3)</span>';
|
||||
/* ---- edge BOM dock: fasteners + merged parts BOM ---- */
|
||||
const SCREWS = /*__SCREWS__*/;
|
||||
const skb = document.querySelector('#screws tbody');
|
||||
const bySize = {};
|
||||
let nTot = 0;
|
||||
for (const s of SCREWS) {
|
||||
nTot += s.数量;
|
||||
bySize[s.螺纹] = (bySize[s.螺纹] || 0) + s.数量;
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = '<td><b>' + s.规格 + '</b><br><small>' + s.头型 + '</small></td>' +
|
||||
'<td>' + s.长度mm + '</td><td>' + s.数量 + '</td>' +
|
||||
'<td>' + s.位置 + '<br><small>' + s.备注 + '</small></td>';
|
||||
skb.appendChild(tr);
|
||||
}
|
||||
const tally = Object.keys(bySize).sort().map(k => k + 'x' + bySize[k]).join(' / ');
|
||||
document.getElementById('scrSum').textContent =
|
||||
'共 ' + SCREWS.length + ' 种 ' + nTot + ' 颗(' + tally + ');另需 M4 螺母 4、M5 螺母 4、M6 螺母+平垫圈各 6,见各行备注';
|
||||
document.getElementById('bomStat').textContent =
|
||||
'紧固件 ' + nTot + ' 颗 / 打印件 ' + DATA.parts.filter(p=>p.printed).length + ' 种';
|
||||
const dock = document.getElementById('bomDock'), bTab = document.getElementById('bomTab');
|
||||
function dockSet(open) {
|
||||
dock.classList.toggle('open', open);
|
||||
bTab.style.display = open ? 'none' : ''; // 展开时拉手藏掉,否则压住抽屉右缘
|
||||
bTab.innerHTML = open ? '收起 <b>清单</b>' : '物料清单 <b>BOM</b>';
|
||||
}
|
||||
bTab.onclick = () => dockSet(!dock.classList.contains('open'));
|
||||
document.getElementById('bomHide').onclick = () => dockSet(false);
|
||||
document.addEventListener('keydown', e => { if (e.key === 'Escape') dockSet(false); });
|
||||
/* ---- toolbar ---- */
|
||||
const bAsm = document.getElementById('bAsm');
|
||||
const bExp = document.getElementById('bExp');
|
||||
const bWire = document.getElementById('bWire');
|
||||
const bXRay = document.getElementById('bXRay');
|
||||
const bL = document.getElementById('bL');
|
||||
const bR = document.getElementById('bR');
|
||||
const sheet = document.getElementById('sheet');
|
||||
const stage = document.getElementById('stage');
|
||||
const on = (id, f) => document.getElementById(id).onclick = f;
|
||||
@@ -532,27 +569,29 @@ on('bWire', () => { bWire.classList.toggle('on'); const w = bWire.classList.cont
|
||||
meshes.forEach(m => { m.material.wireframe = w; }); requestRender(); });
|
||||
on('bXRay', () => { bXRay.classList.toggle('on'); const o = bXRay.classList.contains('on');
|
||||
meshes.forEach(m => { if (!m.userData.printed && m.userData.name[0]==='M') { m.material.opacity = o ? .34 : 1; m.material.transparent = o; } }); requestRender(); });
|
||||
on('bFit', () => { fitTo(BB); az = -0.9; po = 1.15; requestRender(); });
|
||||
on('bFit', () => { fitTo(BB); az = -0.9; po = 1.15; camUp.copy(UP_Y); requestRender(); });
|
||||
const app = document.getElementById('app');
|
||||
on('bL', () => { app.classList.toggle('l'); bL.classList.toggle('on'); });
|
||||
on('bR', () => { app.classList.toggle('r'); bR.classList.toggle('on'); });
|
||||
bL.classList.add('on'); bR.classList.add('on');
|
||||
bL.classList.add('on');
|
||||
document.getElementById('cam').onchange = e => {
|
||||
const k = e.target.value;
|
||||
camUp.copy(UP_Y); // upright roll for every view unless pinned below
|
||||
if (k==='iso'){az=-0.9;po=1.15;} if(k==='front'){az=0;po=1.57;}
|
||||
if(k==='side'){az=1.5708;po=1.57;}
|
||||
// looking down the arm, the height dimension is irrelevant - frame by the PLAN extent
|
||||
if(k==='top'){az=-0.9;po=0.12;rd=Math.max(BB[1][0]-BB[0][0],BB[1][2]-BB[0][2])*1.55+90;}
|
||||
// true plan view: pin the roll (near-vertical lookAt otherwise rolls the image) and
|
||||
// frame by the plan extent - the height dimension is irrelevant looking down
|
||||
if(k==='top'){az=-0.9;po=0.02;camUp.copy(UP_TOP);
|
||||
rd=Math.max(BB[1][0]-BB[0][0],BB[1][2]-BB[0][2])*1.2+180;}
|
||||
const hub = id => { const j = JO.find(q => q.id === id); return j ? VC(j.hub) : [tx, ty, tz]; };
|
||||
if (k === 'wrist') { const a = hub('J5'), b = hub('J6'); // frame the whole hand,
|
||||
tx = (a[0]+b[0])/2; ty = (a[1]+b[1])/2; tz = (a[2]+b[2])/2; rd = 340; } // not just its hub
|
||||
tx = (a[0]+b[0])/2; ty = (a[1]+b[1])/2; tz = (a[2]+b[2])/2; rd = 340; az = -0.6; po = 1.25; }
|
||||
// shoulder close-up has to hold BOTH the roll drum (J1) and the pitch fork (J2) - aiming at
|
||||
// one hub alone filled the frame with a single Ø88 cylinder and showed nothing
|
||||
if (k === 'joint') { const a = hub('J1'), h = hub('J2');
|
||||
tx = (a[0]+h[0])/2; ty = (a[1]+h[1])/2; tz = (a[2]+h[2])/2; rd = 470; }
|
||||
tx = (a[0]+h[0])/2; ty = (a[1]+h[1])/2; tz = (a[2]+h[2])/2; rd = 470; az = -0.6; po = 1.25; }
|
||||
if (k === 'elbow') { const h = hub('J4');
|
||||
tx = h[0]; ty = h[1] - 35; tz = h[2]; rd = 280; az = -0.6; po = 1.25; }
|
||||
if (k === 'hand') { const h = hub('J6'); tx = h[0]; ty = h[1]; tz = h[2] - 45; rd = 215; }
|
||||
if (k === 'hand') { const h = hub('J6'); tx = h[0]; ty = h[1]; tz = h[2] - 45; rd = 215; az = -0.6; po = 1.25; }
|
||||
requestRender();
|
||||
};
|
||||
document.querySelectorAll('.tab[data-view]').forEach(b => b.onclick = () => {
|
||||
|
||||
Reference in New Issue
Block a user