fix(gui): two-click ROI on Calibrazione Piano (was shift+drag)

User asked for "2 punti" — replace the drag-to-rectangle interaction with
two discrete clicks: first click places corner A (cross marker), second
click defines the rectangle, third click starts a new selection.
Rectangles smaller than 10×10 px are rejected. Status chip guides the
flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal PastroandClaude Opus 4.7 committed 2026-05-11 17:38:43 +02:00
1 parent b4db712cd0
commit 652fa4b912
2 files changed
+57 -24

No files matched your search

+51 -19
View File
@@ -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 ----------------