Calibration updates

This commit is contained in:
2026-07-08 12:17:21 +10:00
parent d48a8ce786
commit 2d492baa7d
5 changed files with 473 additions and 7 deletions
+10
View File
@@ -464,6 +464,7 @@ function anchorEditor(a) {
const box = document.createElement('div'); box.style.cssText = 'padding:4px 2px 12px';
box.appendChild(textRow('Label', a.label, (v) => { a.label = v; markDirty(); renderAnchorsSoft(); }));
box.appendChild(selectRow('Marker', a.fiducialType || 'aruco', ['aruco', 'apriltag'], (v) => { a.fiducialType = v; markDirty(); }));
box.appendChild(selectRow('Mount', a.mount || 'flat', ['flat', 'wall'], (v) => { a.mount = v; markDirty(); }));
box.appendChild(numRow('Marker id', a.fiducialId, (v) => { a.fiducialId = v | 0; markDirty(); renderAnchorsSoft(); }, { step: 1, min: 0 }));
box.appendChild(numRow('Size (mm)', a.fiducialSizeMm, (v) => { a.fiducialSizeMm = v; rebuildAnchors(); markDirty(); }, { step: 1 }));
box.appendChild(xyzRow('Pos (mm)', a.position, ['x', 'y', 'z'], () => { rebuildAnchors(); markDirty(); }));
@@ -727,6 +728,15 @@ if (printAnchorsBtn) printAnchorsBtn.addEventListener('click', () => {
window.open('anchors-print.html', '_blank');
});
const planBtn = document.getElementById('planBtn');
if (planBtn) planBtn.addEventListener('click', () => {
if (dirty) toast('Tip: save first so the plan matches your latest layout', '');
window.open('placement-plan.html', '_blank');
});
const calibrateBtn = document.getElementById('calibrateBtn');
if (calibrateBtn) calibrateBtn.addEventListener('click', () => window.open('calibrate.html', '_blank'));
window.addEventListener('beforeunload', (e) => { if (dirty) { e.preventDefault(); e.returnValue = ''; } });
// ===========================================================================
+22 -7
View File
@@ -84,16 +84,31 @@ function markerToCameraMatrix(m) {
return { M, err: pose.bestError, dist: Math.hypot(t[0], t[1], t[2]) };
}
// anchor world placement -> matrix (world coords of the marker's centre + yaw).
// The marker lies flat-ish on the build; we treat its plane with the anchor yaw
// about the vertical (Y) axis. Anchor.position is the marker centre in world mm.
// anchor world placement -> matrix mapping the MARKER's local frame (as POS-IT
// sees it: marker in its XY plane, +Z out of the printed face) into WORLD space.
//
// A crest lying flat on the table has its face pointing UP, so the marker's
// local +Z must map to world +Y. That's a -90 deg rotation about X. On top of
// that we apply the anchor's yaw (spin on the table) and its world position.
//
// mount 'flat' (default): marker lies on the table, face up -> rotX(-90) then yaw about Y
// mount 'wall': marker stands vertical, face outward -> yaw about Y only
//
// The order matters: worldFromMarker = T(pos) * Ry(yaw) * planeTilt.
function anchorToWorldMatrix(anchor) {
const p = anchor.position || { x: 0, y: 0, z: 0 };
const yaw = THREE.MathUtils.degToRad(anchor.rotationDeg?.y || 0);
const m = new THREE.Matrix4();
m.makeRotationY(yaw);
m.setPosition(p.x, p.y, p.z);
return m;
const mount = anchor.mount || 'flat';
const planeTilt = new THREE.Matrix4();
if (mount === 'wall') planeTilt.identity(); // face already points sideways
else planeTilt.makeRotationX(-Math.PI / 2); // flat: tip face up to +Y
const spin = new THREE.Matrix4().makeRotationY(yaw);
const trans = new THREE.Matrix4().setPosition(p.x, p.y, p.z);
// trans * spin * planeTilt
return trans.multiply(spin).multiply(planeTilt);
}
// Given a detected marker with a known anchor, compute world->camera and apply