diff --git a/web/index.html b/web/index.html index e6e9c98..a309b2e 100644 --- a/web/index.html +++ b/web/index.html @@ -187,7 +187,7 @@ -
RGB live · shift+drag = ROI
+
RGB live · 2 click = angoli del rettangolo ROI · 3° click ricomincia

Vista 3D (frame camera)

@@ -198,12 +198,13 @@

Calibrazione

- Disegna con shift+drag un rettangolo sul tavolo (senza oggetti), poi - clicca Calcola piano. Regola l'offset finché i punti - gialli rappresentano l'oggetto e i rossi spariscono. Poi Salva piano. + Clicca due angoli opposti sul tavolo (senza oggetti) per definire il + rettangolo ROI, poi clicca Calcola piano. Regola + l'offset finché i punti gialli rappresentano l'oggetto e i rossi + spariscono. Poi Salva piano.
- +
diff --git a/web/work_plane.js b/web/work_plane.js index 259a901..8be14cc 100644 --- a/web/work_plane.js +++ b/web/work_plane.js @@ -21,7 +21,7 @@ function setChip(text, level) { function setRoiInfo() { const el = $('wpRoiInfo'); - if (!roi) { el.textContent = '— · disegna con shift+drag'; el.classList.add('dim'); return; } + if (!roi) { el.textContent = "— · clicca 2 angoli sull'RGB"; el.classList.add('dim'); return; } el.classList.remove('dim'); el.textContent = `u=${roi.u0}…${roi.u1} v=${roi.v0}…${roi.v1} (${roi.u1 - roi.u0}×${roi.v1 - roi.v0} px)`; } @@ -68,6 +68,19 @@ function startRgbLoop() { } img.addEventListener('load', syncOverlaySize); + // Two-click ROI: 1st click = corner A, 2nd click = corner B (rectangle + // defined). 3rd click starts a new selection. + let firstCorner = null; + + function drawCross(ctx, u, v) { + ctx.strokeStyle = '#9b6dff'; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(u - 8, v); ctx.lineTo(u + 8, v); + ctx.moveTo(u, v - 8); ctx.lineTo(u, v + 8); + ctx.stroke(); + } + function drawOverlay() { const ctx = overlay.getContext('2d'); ctx.clearRect(0, 0, overlay.width, overlay.height); @@ -75,32 +88,51 @@ function startRgbLoop() { ctx.strokeStyle = '#9b6dff'; ctx.lineWidth = 2; ctx.strokeRect(roi.u0, roi.v0, roi.u1 - roi.u0, roi.v1 - roi.v0); + drawCross(ctx, roi.u0, roi.v0); + drawCross(ctx, roi.u1, roi.v1); + } else if (firstCorner) { + drawCross(ctx, firstCorner.u, firstCorner.v); } } - viewport.addEventListener('mousedown', (e) => { - if (!e.shiftKey) return; // shift+drag = ROI; plain drag = pan (no-op here) + viewport.addEventListener('click', (e) => { const p = imgPixelFromEvent(e); - drawing = { u0: p.u, v0: p.v, u1: p.u, v1: p.v }; - e.preventDefault(); - }); - viewport.addEventListener('mousemove', (e) => { - if (!drawing) return; - const p = imgPixelFromEvent(e); - drawing.u1 = p.u; - drawing.v1 = p.v; - roi = { - u0: Math.min(drawing.u0, drawing.u1), - v0: Math.min(drawing.v0, drawing.v1), - u1: Math.max(drawing.u0, drawing.u1), - v1: Math.max(drawing.v0, drawing.v1), - }; + if (firstCorner === null && roi === null) { + // First click: place corner A. + firstCorner = p; + drawOverlay(); + setChip('punto 1/2 piazzato — clicca il secondo angolo'); + return; + } + if (firstCorner !== null) { + // Second click: complete the rectangle. + roi = { + u0: Math.min(firstCorner.u, p.u), + v0: Math.min(firstCorner.v, p.v), + u1: Math.max(firstCorner.u, p.u), + v1: Math.max(firstCorner.v, p.v), + }; + firstCorner = null; + // Reject degenerate rectangles (<10 px on either side). + if (roi.u1 - roi.u0 < 10 || roi.v1 - roi.v0 < 10) { + roi = null; + setChip('rettangolo troppo piccolo, riprova', 'err'); + } else { + setChip('ROI pronto — clicca "Calcola piano"', 'ok'); + } + drawOverlay(); + setRoiInfo(); + updateButtonStates(); + return; + } + // ROI already set: start a new selection on the next click. + roi = null; + firstCorner = p; drawOverlay(); setRoiInfo(); updateButtonStates(); + setChip('punto 1/2 piazzato — clicca il secondo angolo'); }); - viewport.addEventListener('mouseup', () => { drawing = null; }); - viewport.addEventListener('mouseleave', () => { drawing = null; }); })(); // ---------------- Three.js 3D viewer ----------------