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:
1 parent
b4db712cd0
commit
652fa4b912
2 files changed
+57
-24
No files matched your search
+6
-5
@@ -187,7 +187,7 @@
|
||||
<canvas id="wpOverlay" style="position:absolute;left:0;top:0;pointer-events:none"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="marker-info">RGB live · shift+drag = ROI</div>
|
||||
<div class="marker-info">RGB live · 2 click = angoli del rettangolo ROI · 3° click ricomincia</div>
|
||||
<h3 style="margin-top:14px">Vista 3D (frame camera)</h3>
|
||||
<div class="canvas-wrap" style="width:min(100%,720px)">
|
||||
<canvas id="wp3dCanvas" width="720" height="360" style="display:block;background:#0a0d11;border:1px solid var(--border);border-radius:4px"></canvas>
|
||||
@@ -198,12 +198,13 @@
|
||||
<h2>Calibrazione</h2>
|
||||
<div class="ctrl-block">
|
||||
<div class="info-box">
|
||||
Disegna con shift+drag un rettangolo sul tavolo (senza oggetti), poi
|
||||
clicca <strong>Calcola piano</strong>. Regola l'offset finché i punti
|
||||
gialli rappresentano l'oggetto e i rossi spariscono. Poi <strong>Salva piano</strong>.
|
||||
Clicca due angoli opposti sul tavolo (senza oggetti) per definire il
|
||||
rettangolo ROI, poi clicca <strong>Calcola piano</strong>. Regola
|
||||
l'offset finché i punti gialli rappresentano l'oggetto e i rossi
|
||||
spariscono. Poi <strong>Salva piano</strong>.
|
||||
</div>
|
||||
<div class="kv compact">
|
||||
<label>ROI <span id="wpRoiInfo" class="dim">— · disegna con shift+drag</span></label>
|
||||
<label>ROI <span id="wpRoiInfo" class="dim">— · clicca 2 angoli sull'RGB</span></label>
|
||||
<label>offset (mm) <input type="number" id="wpOffset" value="5.0" step="0.5" min="0" max="200" /></label>
|
||||
</div>
|
||||
<div class="actions">
|
||||
|
||||
+51
-19
@@ -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 ----------------
|
||||
|
||||
Reference in new issue
Block a user