44 lines
2.6 KiB
JavaScript
44 lines
2.6 KiB
JavaScript
/* 距离下界认证(2026-10-17,方案 §8.3 / §4.4)
|
||
* 关节直线段 a→b 安全 ⇔ 对每个组对 (gA,gB)(gB ≥ gA+2):
|
||
* gap_ab(a) + gap_ab(b) > Σ_{k=gA}^{gB-1} r_k·|Δθ_k|
|
||
* gap 取 S.gapMatrix(距离 − need,含 margin),r_k 为 S.leverBounds 的每轴杠杆上界(mm/°)。
|
||
* 不满足就二分,子段 < MIN_DEG 仍不过则判不安全。连续轴按 unwrapTo 口径展开后再插值。 */
|
||
'use strict';
|
||
module.exports = function makeCert(S, opt) {
|
||
opt = opt || {};
|
||
const CAP = opt.cap || 40, MIN_DEG = opt.minDeg || 0.002;
|
||
const R = opt.lever || S.leverBounds(4000);
|
||
const L = S.LIM, O = S.ORDER, w = S.wrap180;
|
||
const memo = new Map();
|
||
const key = q => q.map(v => v.toFixed(5)).join(',');
|
||
let queries = 0;
|
||
const gm = q => { const k = key(q); let M = memo.get(k); if (!M) { M = S.gapMatrix(q, CAP); queries++; if (memo.size > 50000) memo.clear(); memo.set(k, M); } return M; };
|
||
const minGap = M => { let m = Infinity; for (let a = 0; a < 7; a++) for (let b = a + 2; b < 7; b++) m = Math.min(m, M[a * 7 + b]); return m; };
|
||
const inBox = q => O.every((id, i) => L[id].cont || (q[i] >= L[id].lo - 1e-6 && q[i] <= L[id].hi + 1e-6));
|
||
function seg(a, b, depth) {
|
||
const Ma = gm(a), Mb = gm(b);
|
||
const dq = a.map((v, i) => Math.abs(b[i] - v));
|
||
let ok = true;
|
||
for (let ga = 0; ga < 7 && ok; ga++) for (let gb = ga + 2; gb < 7; gb++) {
|
||
const lhs = Ma[ga * 7 + gb] + Mb[ga * 7 + gb];
|
||
let rhs = 0; for (let k = ga; k < gb; k++) rhs += R[k] * dq[k];
|
||
if (!(lhs > rhs)) { ok = false; break; }
|
||
}
|
||
if (ok) return true;
|
||
if (minGap(Ma) <= 0 || minGap(Mb) <= 0) return false;
|
||
if (Math.max.apply(null, dq) < MIN_DEG || depth > 40) return false;
|
||
const m = a.map((v, i) => 0.5 * (v + b[i]));
|
||
return seg(a, m, depth + 1) && seg(m, b, depth + 1);
|
||
}
|
||
/* 整条折线:返回 {ok, queries, minGap, failAt} */
|
||
function path(pts) {
|
||
const q0 = queries; let mg = Infinity;
|
||
const P = [pts[0].slice()];
|
||
for (let i = 1; i < pts.length; i++) P.push(P[i - 1].map((v, k) => L[O[k]].cont ? v + w(pts[i][k] - v) : pts[i][k]));
|
||
for (let i = 0; i < P.length; i++) { if (!inBox(P[i])) return { ok: false, queries: queries - q0, minGap: mg, failAt: i, why: 'box' }; mg = Math.min(mg, minGap(gm(P[i]))); }
|
||
for (let i = 1; i < P.length; i++) if (!seg(P[i - 1], P[i], 0)) return { ok: false, queries: queries - q0, minGap: mg, failAt: i, why: 'cert' };
|
||
return { ok: true, queries: queries - q0, minGap: mg, unwrapped: P };
|
||
}
|
||
return { seg: (a, b) => seg(a, b, 0), path, lever: R, gapMin: q => minGap(gm(q)) };
|
||
};
|