diff --git a/web/js/sections/board-control.js b/web/js/sections/board-control.js index d997664..ea0f990 100644 --- a/web/js/sections/board-control.js +++ b/web/js/sections/board-control.js @@ -61,6 +61,19 @@ function buildHTML() { color: var(--accent-red, #ff5252); border: 1px solid rgba(255, 82, 82, 0.3); } + .pos-marker { + position: absolute; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--accent-blue); + border: 2px solid white; + transform: translate(-50%, -50%); + transition: left 0.1s linear, top 0.1s linear; + box-shadow: 0 0 10px rgba(0,0,0,0.5); + z-index: 10; + pointer-events: none; + }
@@ -78,6 +91,7 @@ function buildHTML() {
+
@@ -167,14 +181,37 @@ function buildHTML() { `; } -function updatePositionReadout() { +function updatePosMarker() { + const marker = document.getElementById('pos-marker'); + if (!marker) return; + const t1 = IK.stepsToRad(steps1); const t2 = IK.stepsToRad(steps2); - const { endX, endY } = IK.forward(t1, t2); - const px = document.getElementById('pos-x'); - const py = document.getElementById('pos-y'); - if (px) px.textContent = isFinite(endX) ? endX.toFixed(1) : '---'; - if (py) py.textContent = isFinite(endY) ? endY.toFixed(1) : '---'; + const { endX, endY, valid } = IK.forward(t1, t2); + + if (valid && isFinite(endX) && isFinite(endY)) { + const px = document.getElementById('pos-x'); + const py = document.getElementById('pos-y'); + if (px) px.textContent = endX.toFixed(1); + if (py) py.textContent = endY.toFixed(1); + + const w = bounds.xMax - bounds.xMin; + const h = bounds.yMax - bounds.yMin; + + // Bounds check to avoid marker flying off screen entirely + if (endX >= bounds.xMin && endX <= bounds.xMax && endY >= bounds.yMin && endY <= bounds.yMax) { + const xPct = ((endX - bounds.xMin) / w) * 100; + const yPct = ((bounds.yMax - endY) / h) * 100; + + marker.style.left = `${xPct}%`; + marker.style.top = `${yPct}%`; + marker.style.display = 'block'; + } else { + marker.style.display = 'none'; // Out of bounds visually + } + } else { + marker.style.display = 'none'; + } } function updateHomingUI() { @@ -214,6 +251,7 @@ async function loadSpots() { spots = parsedSpots; document.getElementById('bg-image').src = `web/assets/backgrounds/${currentBg}/bg.svg?v=${Date.now()}`; renderSpots(); + updatePosMarker(); } catch(e) { UI.log('Failed to load spots.json', 'error'); } @@ -301,7 +339,7 @@ export default { () => ({ steps1, steps2 }), (s1, s2) => { steps1 = s1; steps2 = s2; - updatePositionReadout(); + updatePosMarker(); } ); }); @@ -345,7 +383,7 @@ export default { steps2 = IK.ARM.HOME_STEPS.m2; Motion.setSteps(steps1, steps2); saveSteps(); - updatePositionReadout(); + updatePosMarker(); UI.log('[sim] HOMEALL', 'info'); } }); @@ -378,7 +416,7 @@ export default { const [s1, s2] = msg.slice(2).split(',').map(Number); steps1 = s1; steps2 = s2; saveSteps(); - updatePositionReadout(); + updatePosMarker(); } }; document.addEventListener('ble:status', onBLEStatus); @@ -388,7 +426,7 @@ export default { BLE.write('POS').catch(() => {}); sendSettings(); } else { - updatePositionReadout(); + updatePosMarker(); } initQuickSequences( @@ -397,7 +435,7 @@ export default { () => ({ steps1, steps2 }), (s1, s2) => { steps1 = s1; steps2 = s2; - updatePositionReadout(); + updatePosMarker(); } );