logincontroller update
This commit is contained in:
@@ -468,6 +468,10 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
✏️ 3. Disegna profilo
|
||||
</button>
|
||||
|
||||
<button type="button" id="edgeSnapBtn" class="btn btn-outline-warning">
|
||||
🧲 Magnete bordo OFF
|
||||
</button>
|
||||
|
||||
<button type="button" id="toolHoleBtn" class="btn btn-outline-danger">
|
||||
➖ 4. Area da escludere
|
||||
</button>
|
||||
@@ -600,6 +604,13 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
const POINT_HIT_RADIUS = 12;
|
||||
const SEGMENT_HIT_RADIUS = 12;
|
||||
|
||||
let edgeSnapEnabled = false;
|
||||
let edgeSnapPreviewPoint = null;
|
||||
|
||||
const EDGE_SNAP_RADIUS = 45;
|
||||
const EDGE_DARK_THRESHOLD = 245;
|
||||
const EDGE_GRADIENT_THRESHOLD = 8;
|
||||
|
||||
let lastManualResult = null;
|
||||
|
||||
$(document).ready(function() {
|
||||
@@ -1057,6 +1068,15 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
document.getElementById('saveManualAreaBtn').disabled = true;
|
||||
document.getElementById('manualCoordsPreview').innerText = 'Nessun dato calcolato.';
|
||||
|
||||
edgeSnapEnabled = false;
|
||||
|
||||
const edgeSnapBtn = document.getElementById('edgeSnapBtn');
|
||||
if (edgeSnapBtn) {
|
||||
edgeSnapBtn.classList.remove('btn-warning', 'active');
|
||||
edgeSnapBtn.classList.add('btn-outline-warning');
|
||||
edgeSnapBtn.innerText = '🧲 Magnete bordo OFF';
|
||||
}
|
||||
|
||||
setTool('roi');
|
||||
}
|
||||
|
||||
@@ -1100,7 +1120,8 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (selectedEditPoint) {
|
||||
isDraggingEditPoint = true;
|
||||
editDragMoved = false;
|
||||
updateSelectedPointPosition(pos);
|
||||
const finalPos = getSnappedPoint(pos);
|
||||
updateSelectedPointPosition(finalPos);
|
||||
markManualResultDirty();
|
||||
redrawManualOverlay();
|
||||
updateManualPreview();
|
||||
@@ -1141,13 +1162,24 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
if (currentTool === 'edit' && isDraggingEditPoint && selectedEditPoint) {
|
||||
updateSelectedPointPosition(pos);
|
||||
const finalPos = getSnappedPoint(pos);
|
||||
updateSelectedPointPosition(finalPos);
|
||||
editDragMoved = true;
|
||||
markManualResultDirty();
|
||||
redrawManualOverlay();
|
||||
updateManualPreview();
|
||||
return;
|
||||
}
|
||||
if (edgeSnapEnabled && ['polygon', 'hole', 'edit'].includes(currentTool)) {
|
||||
edgeSnapPreviewPoint = snapToNearestEdge(pos, EDGE_SNAP_RADIUS);
|
||||
redrawManualOverlay();
|
||||
return;
|
||||
}
|
||||
|
||||
if (edgeSnapPreviewPoint) {
|
||||
edgeSnapPreviewPoint = null;
|
||||
redrawManualOverlay();
|
||||
}
|
||||
};
|
||||
|
||||
manualOverlayCanvas.onmouseup = function() {
|
||||
@@ -1262,6 +1294,123 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
};
|
||||
}
|
||||
|
||||
function getSnappedPoint(pos) {
|
||||
if (!edgeSnapEnabled) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
if (!manualPdfCanvas || !manualPdfCtx) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
if (!['polygon', 'hole', 'edit'].includes(currentTool)) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
const snapped = snapToNearestEdge(pos, EDGE_SNAP_RADIUS);
|
||||
|
||||
if (snapped) {
|
||||
edgeSnapPreviewPoint = snapped;
|
||||
return snapped;
|
||||
}
|
||||
|
||||
edgeSnapPreviewPoint = null;
|
||||
return pos;
|
||||
}
|
||||
|
||||
function snapToNearestEdge(pos, radius) {
|
||||
const canvasWidth = manualPdfCanvas.width;
|
||||
const canvasHeight = manualPdfCanvas.height;
|
||||
|
||||
const cx = Math.round(pos.x);
|
||||
const cy = Math.round(pos.y);
|
||||
|
||||
const x0 = Math.max(1, cx - radius);
|
||||
const y0 = Math.max(1, cy - radius);
|
||||
const x1 = Math.min(canvasWidth - 2, cx + radius);
|
||||
const y1 = Math.min(canvasHeight - 2, cy + radius);
|
||||
|
||||
const w = x1 - x0 + 1;
|
||||
const h = y1 - y0 + 1;
|
||||
|
||||
if (w <= 3 || h <= 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let imageData;
|
||||
|
||||
try {
|
||||
imageData = manualPdfCtx.getImageData(x0, y0, w, h);
|
||||
} catch (e) {
|
||||
console.warn('Edge snap getImageData failed:', e);
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = imageData.data;
|
||||
|
||||
function grayAt(localX, localY) {
|
||||
const index = (localY * w + localX) * 4;
|
||||
const r = data[index];
|
||||
const g = data[index + 1];
|
||||
const b = data[index + 2];
|
||||
|
||||
return (r + g + b) / 3;
|
||||
}
|
||||
|
||||
let best = null;
|
||||
let bestScore = -Infinity;
|
||||
|
||||
for (let yy = 1; yy < h - 1; yy++) {
|
||||
for (let xx = 1; xx < w - 1; xx++) {
|
||||
const globalX = x0 + xx;
|
||||
const globalY = y0 + yy;
|
||||
|
||||
const dx = globalX - pos.x;
|
||||
const dy = globalY - pos.y;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (dist > radius) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const center = grayAt(xx, yy);
|
||||
|
||||
const gx = Math.abs(grayAt(xx + 1, yy) - grayAt(xx - 1, yy));
|
||||
const gy = Math.abs(grayAt(xx, yy + 1) - grayAt(xx, yy - 1));
|
||||
const gradient = gx + gy;
|
||||
|
||||
const isDarkEnough = center < EDGE_DARK_THRESHOLD;
|
||||
const isRealEdge = gradient > EDGE_GRADIENT_THRESHOLD;
|
||||
|
||||
if (!isDarkEnough && !isRealEdge) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prefer:
|
||||
* - dark strokes
|
||||
* - real contrast edges
|
||||
* - points close to the cursor
|
||||
*/
|
||||
const darknessScore = Math.max(0, 255 - center);
|
||||
const gradientScore = gradient * 3;
|
||||
const distancePenalty = dist * 4;
|
||||
|
||||
const score = darknessScore + gradientScore - distancePenalty;
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
best = {
|
||||
x: globalX,
|
||||
y: globalY
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
function handleCalibrationClick(pos) {
|
||||
if (calibrationPoints.length >= 2) {
|
||||
calibrationPoints = [];
|
||||
@@ -1334,7 +1483,8 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return;
|
||||
}
|
||||
|
||||
polygonPoints.push(pos);
|
||||
const finalPos = getSnappedPoint(pos);
|
||||
polygonPoints.push(finalPos);
|
||||
selectedEditPoint = {
|
||||
type: 'outer',
|
||||
pointIndex: polygonPoints.length - 1
|
||||
@@ -1364,7 +1514,8 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return;
|
||||
}
|
||||
|
||||
currentHolePoints.push(pos);
|
||||
const finalPos = getSnappedPoint(pos);
|
||||
currentHolePoints.push(finalPos);
|
||||
selectedEditPoint = {
|
||||
type: 'currentHole',
|
||||
pointIndex: currentHolePoints.length - 1
|
||||
@@ -1409,6 +1560,32 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
drawCalibration();
|
||||
drawPolygon();
|
||||
drawHoles();
|
||||
drawEdgeSnapPreview();
|
||||
}
|
||||
|
||||
function drawEdgeSnapPreview() {
|
||||
if (!edgeSnapEnabled || !edgeSnapPreviewPoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
manualOverlayCtx.save();
|
||||
|
||||
manualOverlayCtx.strokeStyle = 'rgba(245, 158, 11, 1)';
|
||||
manualOverlayCtx.fillStyle = 'rgba(245, 158, 11, 0.95)';
|
||||
manualOverlayCtx.lineWidth = 2;
|
||||
|
||||
manualOverlayCtx.beginPath();
|
||||
manualOverlayCtx.arc(edgeSnapPreviewPoint.x, edgeSnapPreviewPoint.y, 8, 0, Math.PI * 2);
|
||||
manualOverlayCtx.stroke();
|
||||
|
||||
manualOverlayCtx.beginPath();
|
||||
manualOverlayCtx.moveTo(edgeSnapPreviewPoint.x - 13, edgeSnapPreviewPoint.y);
|
||||
manualOverlayCtx.lineTo(edgeSnapPreviewPoint.x + 13, edgeSnapPreviewPoint.y);
|
||||
manualOverlayCtx.moveTo(edgeSnapPreviewPoint.x, edgeSnapPreviewPoint.y - 13);
|
||||
manualOverlayCtx.lineTo(edgeSnapPreviewPoint.x, edgeSnapPreviewPoint.y + 13);
|
||||
manualOverlayCtx.stroke();
|
||||
|
||||
manualOverlayCtx.restore();
|
||||
}
|
||||
|
||||
function drawRoi() {
|
||||
@@ -1838,9 +2015,11 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return false;
|
||||
}
|
||||
|
||||
const finalPos = getSnappedPoint(pos);
|
||||
|
||||
arr.splice(segment.insertIndex, 0, {
|
||||
x: pos.x,
|
||||
y: pos.y
|
||||
x: finalPos.x,
|
||||
y: finalPos.y
|
||||
});
|
||||
|
||||
selectedEditPoint = {
|
||||
@@ -2411,9 +2590,12 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
function getCanvasMousePosition(event, canvas) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
|
||||
return {
|
||||
x: event.clientX - rect.left,
|
||||
y: event.clientY - rect.top
|
||||
x: (event.clientX - rect.left) * scaleX,
|
||||
y: (event.clientY - rect.top) * scaleY
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2442,6 +2624,33 @@ $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
setTool('polygon');
|
||||
});
|
||||
|
||||
document.getElementById('edgeSnapBtn').addEventListener('click', function() {
|
||||
edgeSnapEnabled = !edgeSnapEnabled;
|
||||
|
||||
const btn = document.getElementById('edgeSnapBtn');
|
||||
|
||||
if (edgeSnapEnabled) {
|
||||
btn.classList.remove('btn-outline-warning');
|
||||
btn.classList.add('btn-warning', 'active');
|
||||
btn.innerText = '🧲 Magnete bordo ON';
|
||||
|
||||
setManualStatus(
|
||||
'Magnete bordo attivo: quando disegni o sposti punti, il sistema prova ad agganciarli al bordo scuro più vicino.'
|
||||
);
|
||||
} else {
|
||||
btn.classList.remove('btn-warning', 'active');
|
||||
btn.classList.add('btn-outline-warning');
|
||||
btn.innerText = '🧲 Magnete bordo OFF';
|
||||
|
||||
edgeSnapPreviewPoint = null;
|
||||
redrawManualOverlay();
|
||||
|
||||
setManualStatus(
|
||||
'Magnete bordo disattivato: i punti verranno inseriti esattamente dove clicchi.'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('undoPointBtn').addEventListener('click', function() {
|
||||
if (currentTool === 'hole' && currentHolePoints.length > 0) {
|
||||
currentHolePoints.pop();
|
||||
|
||||
Reference in New Issue
Block a user