Add files via upload

This commit is contained in:
mercu-lore
2025-11-12 15:38:17 +01:00
committed by GitHub
parent d5f34d6fec
commit bb05cdb1e8
5 changed files with 1235 additions and 667 deletions
+96 -66
View File
@@ -91,15 +91,17 @@ body {
display: flex;
flex: 1;
overflow: hidden;
min-height: 0;
min-width: 0;
}
/* Control Panel */
.control-panel {
width: 320px;
width: 250px;
background-color: var(--panel-bg);
border-right: 1px solid var(--border-color);
overflow-y: auto;
padding: 20px;
padding: 15px;
}
.control-panel h3 {
@@ -177,6 +179,31 @@ input[type="range"]::-moz-range-thumb {
transition: all 0.2s ease;
}
/* Selettore aspect ratio */
.aspect-ratio-select {
width: 100%;
padding: 8px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 13px;
background-color: white;
cursor: pointer;
transition: all 0.2s ease;
}
.aspect-ratio-select:focus {
outline: 2px solid var(--primary-color);
outline-offset: 2px;
}
#aspect-ratio-hint {
display: block;
margin-top: 5px;
font-size: 11px;
color: var(--text-light);
font-style: italic;
}
/* Buttons */
.btn {
padding: 8px 12px;
@@ -228,6 +255,32 @@ input[type="range"]::-moz-range-thumb {
background-color: #475569;
}
.btn-warning {
background-color: var(--warning-color);
border-color: var(--warning-color);
color: white;
width: 100%;
}
.btn-warning:hover:not(:disabled) {
background-color: #c2410c;
border-color: #c2410c;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.pulse-animation {
animation: pulse 2s ease-in-out infinite;
}
.info-box-warning {
background-color: #fff7ed;
border-color: #fed7aa;
}
.btn-success {
background-color: var(--success-color);
color: white;
@@ -305,12 +358,12 @@ input[type="range"]::-moz-range-thumb {
.info-box {
background-color: #f9fafb;
border: 1px solid var(--border-color);
padding: 12px;
padding: 8px;
border-radius: 6px;
font-family: 'Monaco', 'Courier New', monospace;
font-size: 12px;
font-size: 11px;
color: var(--text-color);
line-height: 1.5;
line-height: 1.4;
overflow-x: auto;
}
@@ -331,8 +384,8 @@ input[type="range"]::-moz-range-thumb {
}
.action-buttons .btn {
padding: 10px;
font-size: 13px;
padding: 8px;
font-size: 12px;
font-weight: 600;
}
@@ -356,25 +409,32 @@ hr {
background-color: #fafbfc;
overflow: hidden;
position: relative;
padding: 20px;
min-height: 0;
min-width: 0;
}
#canvas-container {
position: relative;
display: inline-flex;
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
overflow: hidden;
max-width: 90%;
max-height: 90%;
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
}
#background-image {
display: block;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
object-fit: contain;
object-position: center;
user-select: none;
-webkit-user-select: none;
-webkit-user-drag: none;
@@ -470,7 +530,7 @@ hr {
/* Responsive Design */
@media (max-width: 1024px) {
.control-panel {
width: 280px;
width: 220px;
}
}
@@ -523,64 +583,34 @@ textarea:focus {
<div class="selector-content">
<!-- Control Panel -->
<div class="control-panel">
<h3>Controlli</h3>
<h3>Selezione</h3>
<!-- Image Upload -->
<!-- Selettore Aspect Ratio -->
<div class="control-group">
<label for="image-upload">Carica Immagine:</label>
<input type="file" id="image-upload" accept="image/*" style="display: none;">
<button id="upload-btn" class="btn btn-primary">
<span>📁</span> Scegli Immagine
</button>
<small id="image-name">Immagine corrente: picsum.photos</small>
<label for="aspect-ratio-select">Aspect Ratio:</label>
<select id="aspect-ratio-select" class="aspect-ratio-select">
<option value="free" selected>Custom (Free)</option>
<option value="1:1">1:1 Square 1024x1024</option>
<option value="3:4">3:4 Portrait 896x1152</option>
<option value="5:8">5:8 Portrait 832x1216</option>
<option value="9:16">9:16 Portrait 768x1344</option>
<option value="9:21">9:21 Portrait 640x1536</option>
<option value="4:3">4:3 Landscape 1152x896</option>
<option value="3:2">3:2 Landscape 1216x832</option>
<option value="16:9">16:9 Landscape 1344x768</option>
<option value="21:9">21:9 Landscape 1536x640</option>
</select>
<small id="aspect-ratio-hint">Disegno libero - il ratio verrà calcolato</small>
</div>
<hr>
<!-- Zoom Controls -->
<div class="control-group">
<label>Zoom Selezione:</label>
<div class="zoom-buttons">
<button id="zoom-out-btn" class="btn btn-small" disabled>🔍- Zoom Out</button>
<button id="zoom-in-btn" class="btn btn-small" disabled>🔍+ Zoom In</button>
</div>
<div class="zoom-display">
<span id="zoom-value">100%</span>
</div>
<small>Zoom del rettangolo selezionato</small>
<!-- Scale Info Box (nascosto di default) -->
<div id="scale-info" class="info-box info-box-secondary" style="display: none; margin-top: 10px;">
<!-- Popolato dinamicamente da JS -->
</div>
<!-- Border Width -->
<div class="control-group">
<label for="border-slider">
Spessore Bordo: <span id="border-value">3px</span>
</label>
<input type="range" id="border-slider" min="1" max="20" value="3" step="1">
<small>Larghezza linea di selezione</small>
</div>
<!-- Border Position -->
<div class="control-group">
<label>Posizione Bordo:</label>
<div class="radio-group">
<label>
<input type="radio" name="border-position" value="inside" checked>
Interno
</label>
<label>
<input type="radio" name="border-position" value="outside">
Esterno
</label>
</div>
</div>
<hr>
<!-- Reset Button -->
<div class="control-group">
<button id="reset-btn" class="btn btn-secondary" disabled>
🔄 Cancella Selezione
</button>
<!-- Auto-fix Suggestion (nascosto di default) -->
<div id="auto-fix-suggestion" class="info-box" style="display: none; margin-top: 10px; background-color: #fff7ed; border-color: #fed7aa;">
<!-- Popolato dinamicamente da JS -->
</div>
<hr>
@@ -621,6 +651,6 @@ textarea:focus {
</div>
</div>
<script src="/extensions/image_region_select/region_selector_core.js"></script>
<script src="region_selector_core.js"></script>
</body>
</html>
+67 -27
View File
@@ -142,7 +142,11 @@ function handleCanvasMouseDown(e) {
const imgRect = backgroundImage.getBoundingClientRect();
const containerRect = container.getBoundingClientRect();
// Coordinate del click relative all'immagine
// Calcola il fattore di scala (rapporto tra dimensioni visuali e dimensioni reali)
const scaleX = backgroundImage.naturalWidth / imgRect.width;
const scaleY = backgroundImage.naturalHeight / imgRect.height;
// Coordinate del click relative all'immagine (in coordinate visive)
const imgX = e.clientX - imgRect.left;
const imgY = e.clientY - imgRect.top;
@@ -151,6 +155,10 @@ function handleCanvasMouseDown(e) {
return;
}
// Converti a coordinate reali dell'immagine
const realImgX = imgX * scaleX;
const realImgY = imgY * scaleY;
// Verifica se stai ridimensionando
if (currentRectangle) {
const handleRect = detectResizeHandle(e, currentRectangle);
@@ -178,16 +186,20 @@ function handleCanvasMouseDown(e) {
}
}
// Inizia nuovo rettangolo
// Inizia nuovo rettangolo (usando coordinate reali)
isDrawing = true;
startX = imgX;
startY = imgY;
baseX = imgX;
baseY = imgY;
startX = realImgX;
startY = realImgY;
baseX = realImgX;
baseY = realImgY;
baseWidth = 1;
baseHeight = 1;
createNewRectangle(imgX, imgY, 1, 1);
// Salva i fattori di scala globali per uso in handleMouseMove
window.scaleX = scaleX;
window.scaleY = scaleY;
createNewRectangle(realImgX, realImgY, 1, 1);
}
function handleMouseMove(e) {
@@ -196,16 +208,24 @@ function handleMouseMove(e) {
const imgRect = backgroundImage.getBoundingClientRect();
// Usa i fattori di scala salvati o calcolali se non disponibili
const scaleX = window.scaleX || (backgroundImage.naturalWidth / imgRect.width);
const scaleY = window.scaleY || (backgroundImage.naturalHeight / imgRect.height);
if (isResizing && currentRectangle && resizingEdge) {
handleResizeRectangle(e, imgRect);
handleResizeRectangle(e, imgRect, scaleX, scaleY);
} else if (isDrawing) {
const imgX = e.clientX - imgRect.left;
const imgY = e.clientY - imgRect.top;
const width = Math.max(10, Math.abs(imgX - startX));
const height = Math.max(10, Math.abs(imgY - startY));
const x = Math.min(startX, imgX);
const y = Math.min(startY, imgY);
// Converti a coordinate reali
const realImgX = imgX * scaleX;
const realImgY = imgY * scaleY;
const width = Math.max(10, Math.abs(realImgX - startX));
const height = Math.max(10, Math.abs(realImgY - startY));
const x = Math.min(startX, realImgX);
const y = Math.min(startY, realImgY);
baseX = x;
baseY = y;
@@ -232,12 +252,22 @@ function createNewRectangle(x, y, width, height) {
currentRectangle.remove();
}
// Calcola le coordinate visive basate sulle coordinate reali
const imgRect = backgroundImage.getBoundingClientRect();
const scaleX = backgroundImage.naturalWidth / imgRect.width;
const scaleY = backgroundImage.naturalHeight / imgRect.height;
const visualX = x / scaleX;
const visualY = y / scaleY;
const visualWidth = width / scaleX;
const visualHeight = height / scaleY;
const rect = document.createElement('div');
rect.className = `rectangle border-${borderPosition}`;
rect.style.left = x + 'px';
rect.style.top = y + 'px';
rect.style.width = width + 'px';
rect.style.height = height + 'px';
rect.style.left = visualX + 'px';
rect.style.top = visualY + 'px';
rect.style.width = visualWidth + 'px';
rect.style.height = visualHeight + 'px';
rect.style.borderWidth = currentBorderWidth + 'px';
// Aggiungi resize handles
@@ -259,17 +289,27 @@ function createNewRectangle(x, y, width, height) {
function updateRectanglePosition(x, y, width, height) {
if (!currentRectangle) return;
currentRectangle.style.left = x + 'px';
currentRectangle.style.top = y + 'px';
currentRectangle.style.width = width + 'px';
currentRectangle.style.height = height + 'px';
// Calcola le coordinate visive basate sulle coordinate reali
const imgRect = backgroundImage.getBoundingClientRect();
const scaleX = backgroundImage.naturalWidth / imgRect.width;
const scaleY = backgroundImage.naturalHeight / imgRect.height;
const visualX = x / scaleX;
const visualY = y / scaleY;
const visualWidth = width / scaleX;
const visualHeight = height / scaleY;
currentRectangle.style.left = visualX + 'px';
currentRectangle.style.top = visualY + 'px';
currentRectangle.style.width = visualWidth + 'px';
currentRectangle.style.height = visualHeight + 'px';
}
function handleResizeRectangle(e, imgRect) {
function handleResizeRectangle(e, imgRect, scaleX, scaleY) {
if (!currentRectangle) return;
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
const deltaX = (e.clientX - startX) * scaleX;
const deltaY = (e.clientY - startY) * scaleY;
let newX = rectStartX;
let newY = rectStartY;
@@ -424,10 +464,10 @@ function updateAllDimensions() {
<strong>Altezza:</strong> ${Math.round(baseHeight)} px<br>
<br>
<strong>Percentuali:</strong><br>
<strong>X%:</strong> ${((baseX / backgroundImage.width) * 100).toFixed(2)}%<br>
<strong>Y%:</strong> ${((baseY / backgroundImage.height) * 100).toFixed(2)}%<br>
<strong>W%:</strong> ${((baseWidth / backgroundImage.width) * 100).toFixed(2)}%<br>
<strong>H%:</strong> ${((baseHeight / backgroundImage.height) * 100).toFixed(2)}%
<strong>X%:</strong> ${((baseX / backgroundImage.naturalWidth) * 100).toFixed(2)}%<br>
<strong>Y%:</strong> ${((baseY / backgroundImage.naturalHeight) * 100).toFixed(2)}%<br>
<strong>W%:</strong> ${((baseWidth / backgroundImage.naturalWidth) * 100).toFixed(2)}%<br>
<strong>H%:</strong> ${((baseHeight / backgroundImage.naturalHeight) * 100).toFixed(2)}%
`;
baseCoordinates.innerHTML = baseInfo;
File diff suppressed because it is too large Load Diff
+514 -58
View File
@@ -36,6 +36,7 @@ function initializeCanvasSelector(container, imageUrl) {
// Variabili di stato
let isDrawing = false;
let isResizing = false;
let isDragging = false;
let resizingEdge = null;
let startX = 0;
let startY = 0;
@@ -43,6 +44,8 @@ function initializeCanvasSelector(container, imageUrl) {
let rectStartY = 0;
let rectStartWidth = 0;
let rectStartHeight = 0;
let dragOffsetX = 0;
let dragOffsetY = 0;
let currentRectangle = null;
let currentBorderWidth = 0;
let borderPosition = 'inside';
@@ -54,13 +57,49 @@ function initializeCanvasSelector(container, imageUrl) {
let baseX = 0;
let baseY = 0;
// Aspect Ratio Mode
let aspectRatioMode = "free"; // "free" o ratio specifico (es. "16:9")
let aspectRatioValue = null; // Valore numerico (es. 16/9 = 1.777)
// Fix Image Size - tracking dello stato
let displayScaleFactor = 1.0; // Fattore di scala applicato alla preview
let isImageFixed = false; // True quando immagine è stata "fixata"
const defaultBorderWidth = 3;
// Carica l'immagine iniziale
// ========================================================
// Ridimensionamento immagini > 1024px lato backend
// ========================================================
if (imageUrl) {
backgroundImage.src = imageUrl;
const filename = imageUrl.split('/').pop();
imageName.textContent = `Image: ${filename}`;
fetch("/region_selector/scale", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ filename })
})
.then(res => res.json())
.then(data => {
if (data.path) {
backgroundImage.src = data.path;
backgroundImage.dataset.scaleFactor = data.scale || 1;
backgroundImage.dataset.scaled = data.scaled || false;
console.log(`[RegionSelectorExt] Immagine caricata - Scale: ${data.scale || 1}`);
} else if (data.error) {
console.warn(`[RegionSelectorExt] Scale error: ${data.error}`);
backgroundImage.src = imageUrl;
backgroundImage.dataset.scaleFactor = 1;
backgroundImage.dataset.scaled = false;
}
imageName.textContent = `Image: ${filename}`;
})
.catch(e => {
console.error("[RegionSelectorExt] Errore scale:", e);
backgroundImage.src = imageUrl;
backgroundImage.dataset.scaleFactor = 1;
backgroundImage.dataset.scaled = false;
imageName.textContent = `Image: ${filename}`;
});
}
// Disabilita drag dell'immagine
@@ -81,8 +120,158 @@ function initializeCanvasSelector(container, imageUrl) {
}
});
// Mouse down - inizio disegno
// Aspect Ratio Mode Selector
const aspectRatioSelect = container.querySelector('#aspect-ratio-select');
const aspectRatioHint = container.querySelector('#aspect-ratio-hint');
if (aspectRatioSelect) {
aspectRatioSelect.addEventListener('change', (e) => {
aspectRatioMode = e.target.value;
// Calcola valore numerico
const ratioMap = {
"free": null,
"1:1": 1/1,
"3:4": 3/4,
"5:8": 5/8,
"9:16": 9/16,
"9:21": 9/21,
"4:3": 4/3,
"3:2": 3/2,
"16:9": 16/9,
"21:9": 21/9,
};
aspectRatioValue = ratioMap[aspectRatioMode];
// Aggiorna hint
if (aspectRatioMode === "free") {
aspectRatioHint.textContent = "Disegno libero - il ratio verrà calcolato";
aspectRatioHint.style.color = "#64748b";
aspectRatioHint.style.fontWeight = "normal";
} else {
aspectRatioHint.textContent = `Rettangolo vincolato a ${aspectRatioMode}`;
aspectRatioHint.style.color = "#16a34a";
aspectRatioHint.style.fontWeight = "600";
}
console.log(`[AspectRatio] Mode: ${aspectRatioMode}, Value: ${aspectRatioValue}`);
// Se c'è già un rettangolo e passi a modalità vincolata, adattalo
if (rectangleExists && aspectRatioValue !== null) {
adjustRectangleToAspectRatio();
}
});
}
// ========================================================
// FIX IMAGE SIZE - BOTTONE DINAMICO
// ========================================================
function createFixImageButton() {
// Bottone rimosso - la scala viene applicata automaticamente
// Niente da fare qui
console.log('[FixImage] Scale auto-applied, no button needed');
}
function fixImageScale() {
const naturalW = backgroundImage.naturalWidth;
const naturalH = backgroundImage.naturalHeight;
const maxDim = 1024;
const maxCurrent = Math.max(naturalW, naturalH);
if (maxCurrent <= maxDim) {
alert("✓ Immagine già piccola!");
return;
}
displayScaleFactor = maxDim / maxCurrent;
const newW = Math.round(naturalW * displayScaleFactor);
const newH = Math.round(naturalH * displayScaleFactor);
console.log(`[FixImage] Scaling ${naturalW}x${naturalH} → ${newW}x${newH}`);
backgroundImage.style.width = `${newW}px`;
backgroundImage.style.height = `${newH}px`;
backgroundImage.style.maxWidth = 'none';
backgroundImage.style.maxHeight = 'none';
isImageFixed = true;
const fixImageBtn = container.querySelector('#fix-image-btn');
if (fixImageBtn) {
fixImageBtn.textContent = "🔄 Reset Scale";
fixImageBtn.classList.remove('btn-primary');
fixImageBtn.classList.add('btn-warning');
}
const scaleInfo = container.querySelector('#scale-info');
if (scaleInfo) {
const scalePercent = (displayScaleFactor * 100).toFixed(1);
scaleInfo.innerHTML = `
<p><strong>📊 Preview Scale:</strong> ${scalePercent}%</p>
<p><strong>🖼️ Display Size:</strong> ${newW} × ${newH} px</p>
<p><strong>📐 Original Size:</strong> ${naturalW} × ${naturalH} px</p>
<p style="color: #16a34a; font-weight: 600; margin-top: 8px;">✓ Selezione fluida attiva</p>
`;
scaleInfo.style.display = 'block';
}
}
function resetImageScale() {
displayScaleFactor = 1.0;
console.log("[FixImage] Resetting to original scale");
backgroundImage.style.width = 'auto';
backgroundImage.style.height = 'auto';
backgroundImage.style.maxWidth = '100%';
backgroundImage.style.maxHeight = '100%';
isImageFixed = false;
const fixImageBtn = container.querySelector('#fix-image-btn');
if (fixImageBtn) {
fixImageBtn.textContent = "⚡ Fix Image Size";
fixImageBtn.classList.remove('btn-warning');
fixImageBtn.classList.add('btn-primary');
}
const scaleInfo = container.querySelector('#scale-info');
if (scaleInfo) {
scaleInfo.style.display = 'none';
}
}
// Auto-fixa immagine se è grande (> 1024px)
setTimeout(() => {
const naturalW = backgroundImage.naturalWidth;
const naturalH = backgroundImage.naturalHeight;
const maxDim = Math.max(naturalW, naturalH);
console.log(`[FixImage] Image size: ${naturalW}x${naturalH}, max: ${maxDim}`);
if (maxDim > 1024) {
console.log('[FixImage] Large image detected, creating button and auto-fixing scale...');
createFixImageButton();
// Applica la scala automaticamente
setTimeout(() => {
fixImageScale();
}, 100);
}
}, 500);
// Mouse down - inizio disegno o drag
canvasContainer.addEventListener('mousedown', (e) => {
// Se il rettangolo esiste e clicchi su di esso (non su un handle), inizia il drag
if (rectangleExists && e.target === currentRectangle) {
isDragging = true;
const rect = canvasContainer.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
dragOffsetX = mouseX - parseFloat(currentRectangle.style.left);
dragOffsetY = mouseY - parseFloat(currentRectangle.style.top);
canvasContainer.style.cursor = 'grab';
return;
}
if (rectangleExists) return;
if (e.target.classList.contains('resize-handle')) return;
@@ -95,6 +284,23 @@ function initializeCanvasSelector(container, imageUrl) {
currentRectangle = document.createElement('div');
currentRectangle.className = 'rectangle';
// Se sei in modalità vincolata, resetta a free (l'utente sta facendo una nuova selezione)
if (aspectRatioMode !== "free") {
const aspectRatioSelect = container.querySelector('#aspect-ratio-select');
if (aspectRatioSelect) {
aspectRatioSelect.value = "free";
aspectRatioMode = "free";
aspectRatioValue = null;
const aspectRatioHint = container.querySelector('#aspect-ratio-hint');
if (aspectRatioHint) {
aspectRatioHint.textContent = "Disegno libero - il ratio verrà calcolato";
aspectRatioHint.style.color = "#64748b";
aspectRatioHint.style.fontWeight = "normal";
}
console.log("[AspectRatio] Reset to FREE mode for new selection");
}
}
if (borderPosition === 'outside') {
currentRectangle.classList.add('border-outside');
currentRectangle.style.outlineWidth = currentBorderWidth + 'px';
@@ -119,27 +325,69 @@ function initializeCanvasSelector(container, imageUrl) {
// Mouse move
document.addEventListener('mousemove', (e) => {
// Drag del rettangolo
if (isDragging && currentRectangle) {
const rect = canvasContainer.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
let newLeft = mouseX - dragOffsetX;
let newTop = mouseY - dragOffsetY;
// Vincola il rettangolo dentro il canvas
const maxLeft = rect.width - parseFloat(currentRectangle.style.width);
const maxTop = rect.height - parseFloat(currentRectangle.style.height);
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
newTop = Math.max(0, Math.min(newTop, maxTop));
currentRectangle.style.left = newLeft + 'px';
currentRectangle.style.top = newTop + 'px';
baseX = newLeft;
baseY = newTop;
updateAllDimensions();
canvasContainer.style.cursor = 'grabbing';
return;
}
if (isDrawing) {
const rect = canvasContainer.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
const width = currentX - startX;
const height = currentY - startY;
let width = currentX - startX;
let height = currentY - startY;
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
// Modalità vincolata: forza l'aspect ratio
if (Math.abs(width) / aspectRatioValue > Math.abs(height)) {
height = (Math.abs(width) / aspectRatioValue) * (height < 0 ? -1 : 1);
} else {
width = (Math.abs(height) * aspectRatioValue) * (width < 0 ? -1 : 1);
}
}
baseWidth = Math.abs(width);
baseHeight = Math.abs(height);
if (width < 0) {
currentRectangle.style.left = currentX + 'px';
currentRectangle.style.left = (currentX + width) + 'px';
currentRectangle.style.width = Math.abs(width) + 'px';
} else {
currentRectangle.style.width = width + 'px';
}
if (height < 0) {
currentRectangle.style.top = currentY + 'px';
currentRectangle.style.top = (currentY + height) + 'px';
currentRectangle.style.height = Math.abs(height) + 'px';
} else {
currentRectangle.style.height = height + 'px';
}
updateAllDimensions();
}
if (isResizing && currentRectangle) {
@@ -147,79 +395,185 @@ function initializeCanvasSelector(container, imageUrl) {
const deltaY = e.clientY - startY;
switch(resizingEdge) {
case 'top':
const newTop = rectStartY + deltaY;
const newHeight = rectStartHeight - deltaY;
if (newHeight > 0) {
currentRectangle.style.top = newTop + 'px';
currentRectangle.style.height = newHeight + 'px';
case 'bottom-right':
let brNewWidth = rectStartWidth + deltaX;
let brNewHeight = rectStartHeight + deltaY;
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
if (brNewWidth / aspectRatioValue > brNewHeight) {
brNewHeight = brNewWidth / aspectRatioValue;
} else {
brNewWidth = brNewHeight * aspectRatioValue;
}
}
if (brNewHeight > 0 && brNewWidth > 0) {
currentRectangle.style.width = brNewWidth + 'px';
currentRectangle.style.height = brNewHeight + 'px';
baseWidth = brNewWidth;
baseHeight = brNewHeight;
}
break;
case 'bottom':
const bottomHeight = rectStartHeight + deltaY;
if (bottomHeight > 0) {
currentRectangle.style.height = bottomHeight + 'px';
case 'bottom-left':
let blNewLeft = rectStartX + deltaX;
let blNewHeight = rectStartHeight + deltaY;
let blNewWidth = rectStartWidth - deltaX;
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
if (blNewWidth / aspectRatioValue > blNewHeight) {
blNewHeight = blNewWidth / aspectRatioValue;
} else {
blNewWidth = blNewHeight * aspectRatioValue;
blNewLeft = rectStartX + (rectStartWidth - blNewWidth);
}
}
if (blNewHeight > 0 && blNewWidth > 0) {
currentRectangle.style.left = blNewLeft + 'px';
currentRectangle.style.height = blNewHeight + 'px';
currentRectangle.style.width = blNewWidth + 'px';
baseX = blNewLeft;
baseWidth = blNewWidth;
baseHeight = blNewHeight;
}
break;
case 'left':
const newLeft = rectStartX + deltaX;
const newWidth = rectStartWidth - deltaX;
if (newWidth > 0) {
currentRectangle.style.left = newLeft + 'px';
currentRectangle.style.width = newWidth + 'px';
}
break;
case 'top-right':
let trNewTop = rectStartY + deltaY;
let trNewHeight = rectStartHeight - deltaY;
let trNewWidth = rectStartWidth + deltaX;
case 'right':
const rightWidth = rectStartWidth + deltaX;
if (rightWidth > 0) {
currentRectangle.style.width = rightWidth + 'px';
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
if (trNewWidth / aspectRatioValue > trNewHeight) {
trNewHeight = trNewWidth / aspectRatioValue;
trNewTop = rectStartY + (rectStartHeight - trNewHeight);
} else {
trNewWidth = trNewHeight * aspectRatioValue;
}
}
if (trNewHeight > 0 && trNewWidth > 0) {
currentRectangle.style.top = trNewTop + 'px';
currentRectangle.style.height = trNewHeight + 'px';
currentRectangle.style.width = trNewWidth + 'px';
baseY = trNewTop;
baseWidth = trNewWidth;
baseHeight = trNewHeight;
}
break;
case 'top-left':
const tlNewTop = rectStartY + deltaY;
const tlNewLeft = rectStartX + deltaX;
const tlNewHeight = rectStartHeight - deltaY;
const tlNewWidth = rectStartWidth - deltaX;
let tlNewTop = rectStartY + deltaY;
let tlNewLeft = rectStartX + deltaX;
let tlNewHeight = rectStartHeight - deltaY;
let tlNewWidth = rectStartWidth - deltaX;
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
if (tlNewWidth / aspectRatioValue > tlNewHeight) {
tlNewHeight = tlNewWidth / aspectRatioValue;
tlNewTop = rectStartY + (rectStartHeight - tlNewHeight);
} else {
tlNewWidth = tlNewHeight * aspectRatioValue;
tlNewLeft = rectStartX + (rectStartWidth - tlNewWidth);
}
}
if (tlNewHeight > 0 && tlNewWidth > 0) {
currentRectangle.style.top = tlNewTop + 'px';
currentRectangle.style.left = tlNewLeft + 'px';
currentRectangle.style.height = tlNewHeight + 'px';
currentRectangle.style.width = tlNewWidth + 'px';
baseX = tlNewLeft;
baseY = tlNewTop;
baseWidth = tlNewWidth;
baseHeight = tlNewHeight;
}
break;
case 'top-right':
const trNewTop = rectStartY + deltaY;
const trNewHeight = rectStartHeight - deltaY;
const trNewWidth = rectStartWidth + deltaX;
if (trNewHeight > 0 && trNewWidth > 0) {
currentRectangle.style.top = trNewTop + 'px';
currentRectangle.style.height = trNewHeight + 'px';
currentRectangle.style.width = trNewWidth + 'px';
case 'right':
let rightWidth = rectStartWidth + deltaX;
let rightHeight = rightWidth / aspectRatioValue || rectStartHeight;
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
rightHeight = rightWidth / aspectRatioValue;
}
if (rightWidth > 0) {
currentRectangle.style.width = rightWidth + 'px';
if (aspectRatioValue !== null && rightHeight > 0) {
currentRectangle.style.height = rightHeight + 'px';
baseWidth = rightWidth;
baseHeight = rightHeight;
}
}
break;
case 'bottom-left':
const blNewLeft = rectStartX + deltaX;
const blNewHeight = rectStartHeight + deltaY;
const blNewWidth = rectStartWidth - deltaX;
if (blNewHeight > 0 && blNewWidth > 0) {
currentRectangle.style.left = blNewLeft + 'px';
currentRectangle.style.height = blNewHeight + 'px';
currentRectangle.style.width = blNewWidth + 'px';
case 'left':
let newLeft = rectStartX + deltaX;
let newWidth = rectStartWidth - deltaX;
let newHeight = newWidth / aspectRatioValue || rectStartHeight;
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
newHeight = newWidth / aspectRatioValue;
}
if (newWidth > 0) {
currentRectangle.style.left = newLeft + 'px';
currentRectangle.style.width = newWidth + 'px';
if (aspectRatioValue !== null && newHeight > 0) {
currentRectangle.style.height = newHeight + 'px';
baseX = newLeft;
baseWidth = newWidth;
baseHeight = newHeight;
}
}
break;
case 'bottom-right':
const brNewWidth = rectStartWidth + deltaX;
const brNewHeight = rectStartHeight + deltaY;
if (brNewHeight > 0 && brNewWidth > 0) {
currentRectangle.style.width = brNewWidth + 'px';
currentRectangle.style.height = brNewHeight + 'px';
case 'bottom':
let bottomHeight = rectStartHeight + deltaY;
let bottomWidth = bottomHeight * aspectRatioValue || rectStartWidth;
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
bottomWidth = bottomHeight * aspectRatioValue;
}
if (bottomHeight > 0) {
currentRectangle.style.height = bottomHeight + 'px';
if (aspectRatioValue !== null && bottomWidth > 0) {
currentRectangle.style.width = bottomWidth + 'px';
baseWidth = bottomWidth;
baseHeight = bottomHeight;
}
}
break;
case 'top':
let newTop = rectStartY + deltaY;
let topNewHeight = rectStartHeight - deltaY;
let topNewWidth = topNewHeight * aspectRatioValue || rectStartWidth;
// ⚙️ APPLICA VINCOLO SE NECESSARIO
if (aspectRatioValue !== null) {
topNewWidth = topNewHeight * aspectRatioValue;
}
if (topNewHeight > 0) {
currentRectangle.style.top = newTop + 'px';
currentRectangle.style.height = topNewHeight + 'px';
if (aspectRatioValue !== null && topNewWidth > 0) {
currentRectangle.style.width = topNewWidth + 'px';
baseY = newTop;
baseWidth = topNewWidth;
baseHeight = topNewHeight;
}
}
break;
}
@@ -271,6 +625,11 @@ function initializeCanvasSelector(container, imageUrl) {
resizingEdge = null;
canvasContainer.style.cursor = 'default';
}
if (isDragging) {
isDragging = false;
canvasContainer.style.cursor = 'default';
}
});
function resetRectangle() {
@@ -286,6 +645,21 @@ function initializeCanvasSelector(container, imageUrl) {
baseCoordinates.innerHTML = '<p>Click and drag to select</p>';
}
function adjustRectangleToAspectRatio() {
if (!aspectRatioValue || !rectangleExists || !currentRectangle) return;
// Mantieni larghezza, adatta altezza al ratio
baseHeight = baseWidth / aspectRatioValue;
// Aggiorna visualizzazione
currentRectangle.style.width = baseWidth + 'px';
currentRectangle.style.height = baseHeight + 'px';
updateAllDimensions();
console.log(`[AspectRatio] Adjusted to ${aspectRatioMode}: ${Math.round(baseWidth)}x${Math.round(baseHeight)}`);
}
function addResizeHandles() {
const handles = ['top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'];
handles.forEach(position => {
@@ -319,19 +693,75 @@ function initializeCanvasSelector(container, imageUrl) {
const baseX2 = baseX + baseWidth;
const baseY2 = baseY + baseHeight;
const w = Math.round(baseWidth);
const h = Math.round(baseHeight);
const ratio = w / h;
// 🎯 MODALITÀ CUSTOM: Calcola approssimazione
let aspectRatioDisplay;
if (aspectRatioMode === "free") {
// Lista aspect ratio standard
const standardRatios = [
{ value: 21/9, label: "21:9 Landscape", display: "21:9" },
{ value: 16/9, label: "16:9 Landscape", display: "16:9" },
{ value: 3/2, label: "3:2 Landscape", display: "3:2" },
{ value: 4/3, label: "4:3 Landscape", display: "4:3" },
{ value: 1/1, label: "1:1 Square", display: "1:1" },
{ value: 3/4, label: "3:4 Portrait", display: "3:4" },
{ value: 5/8, label: "5:8 Portrait", display: "5:8" },
{ value: 9/16, label: "9:16 Portrait", display: "9:16" },
{ value: 9/21, label: "9:21 Portrait", display: "9:21" },
];
// Trova il più vicino
let closestRatio = standardRatios[0];
let minDiff = Math.abs(ratio - standardRatios[0].value);
for (const r of standardRatios) {
const diff = Math.abs(ratio - r.value);
if (diff < minDiff) {
minDiff = diff;
closestRatio = r;
}
}
const diffPercent = (minDiff / ratio) * 100;
// Formato display basato su vicinanza
if (diffPercent < 3) {
// Molto vicino - mostra come esatto
aspectRatioDisplay = `<span style="color: #16a34a; font-weight: bold;">✓ ${closestRatio.label}</span>`;
} else if (diffPercent < 8) {
// Abbastanza vicino - mostra approssimato
aspectRatioDisplay = `<span style="color: #ea580c; font-weight: bold;">~ ${closestRatio.label}</span> <span style="opacity: 0.6; font-size: 11px;">(${ratio.toFixed(2)}:1)</span>`;
} else {
// Troppo diverso - mostra custom + più vicino
aspectRatioDisplay = `<span style="color: #2563eb; font-weight: bold;">${ratio.toFixed(2)}:1</span> <span style="opacity: 0.5; font-size: 11px;">(vicino: ${closestRatio.display})</span>`;
}
} else {
// 🔒 MODALITÀ VINCOLATA: Mostra il vincolo attivo
aspectRatioDisplay = `<span style="color: #16a34a; font-weight: bold;">🔒 ${aspectRatioMode}</span> <span style="opacity: 0.6; font-size: 11px;">(${ratio.toFixed(2)}:1)</span>`;
}
baseCoordinates.innerHTML = `
<strong>📍 Coordinates:</strong><br>
x1 = ${Math.round(baseX1)}px, y1 = ${Math.round(baseY1)}px<br>
x2 = ${Math.round(baseX2)}px, y2 = ${Math.round(baseY2)}px<br>
<br>
<strong>📏 Size:</strong><br>
Width: ${Math.round(baseWidth)}px<br>
Height: ${Math.round(baseHeight)}px
Width: ${w}px<br>
Height: ${h}px<br>
<br>
<strong>✨ Aspect Ratio:</strong> ${aspectRatioDisplay}
`;
}
return {
getCoordinates: () => {
// Leggi il fattore di scala salvato dal backend
const scaleFactor = parseFloat(backgroundImage.dataset.scaleFactor || "1");
const baseX1 = baseX;
const baseY1 = baseY;
const baseX2 = baseX + baseWidth;
@@ -361,6 +791,7 @@ function initializeCanvasSelector(container, imageUrl) {
y2: Math.round(effectiveY2),
borderWidth: currentBorderWidth,
borderPosition: borderPosition,
displayScaleFactor: displayScaleFactor,
};
},
getState: () => ({
@@ -621,6 +1052,31 @@ async function openRegionDialog(node, app) {
<small id="image-name" style="display: block; font-size: 12px; color: #64748b;">Image: ${imageName}</small>
</div>
<!-- Aspect Ratio Selector -->
<div style="margin-bottom: 20px;">
<label for="aspect-ratio-select" style="display: block; font-size: 12px; font-weight: 600; margin-bottom: 8px;">Aspect Ratio:</label>
<select id="aspect-ratio-select" style="
width: 100%;
padding: 8px;
border: 1px solid #e2e8f0;
border-radius: 6px;
font-size: 12px;
background-color: white;
cursor: pointer;
">
<option value="free" selected>Custom (Free)</option>
<option value="1:1">1:1 Square</option>
<option value="3:4">3:4 Portrait</option>
<option value="5:8">5:8 Portrait</option>
<option value="9:16">9:16 Portrait</option>
<option value="9:21">9:21 Portrait</option>
<option value="4:3">4:3 Landscape</option>
<option value="3:2">3:2 Landscape</option>
<option value="16:9">16:9 Landscape</option>
<option value="21:9">21:9 Landscape</option>
</select>
<small id="aspect-ratio-hint" style="display: block; margin-top: 5px; font-size: 11px; color: #64748b; font-style: italic;">Disegno libero - il ratio verrà calcolato</small>
</div>
<!-- Reset Button -->
<button id="reset-btn" class="btn" disabled style="
+48 -7
View File
@@ -228,16 +228,57 @@ function handleSelectorCancel(modal) {
/**
* Carica un'immagine come sfondo nell'interfaccia
* (Opzionale - per preview)
* Ridimensiona automaticamente se > 1024px
*/
function loadBackgroundImage(node, widthWidget, heightWidget) {
async function loadBackgroundImage(node, widthWidget, heightWidget) {
try {
// Cerca un nodo con input immagine nel flusso
// Per ora, usa l'immagine default di picsum.photos
const bgImg = document.getElementById("background-image");
if (bgImg) {
// Opzionalmente puoi aggiungere logica per caricare immagini
console.log("Image loader ready");
if (!bgImg) {
console.warn("background-image element not found");
return;
}
// Cerca il widget immagine del nodo
const imageWidget = node.widgets.find(w => w.type === "image" || w.name === "image");
if (!imageWidget || !imageWidget.value) {
console.warn("No image widget found on node");
return;
}
// Estrai il filename dall'immagine
let filename = imageWidget.value;
if (typeof filename === "object" && filename.name) {
filename = filename.name;
}
console.log(`[RegionSelector] Caricamento immagine: ${filename}`);
// Chiama l'endpoint per scalare se necessario
const scaleResponse = await fetch("/region_selector/scale", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ filename: filename })
});
const scaleResult = await scaleResponse.json();
console.log(`[RegionSelector] Scale result:`, scaleResult);
if (scaleResult.path) {
// Carica l'immagine (originale o scalata) nel Region Selector
bgImg.src = scaleResult.path;
// Salva i dati per il calcolo delle coordinate
if (scaleResult.scaled) {
bgImg.dataset.scaled = "true";
bgImg.dataset.scaleFactor = scaleResult.scale || 1;
console.log(`[RegionSelector] Immagine scalata caricata, scale factor: ${scaleResult.scale}`);
} else {
bgImg.dataset.scaled = "false";
bgImg.dataset.scaleFactor = 1;
console.log(`[RegionSelector] Immagine originale caricata (≤ 1024px)`);
}
} else if (scaleResult.error) {
console.warn(`[RegionSelector] Scale error: ${scaleResult.error}`);
}
} catch (e) {
console.warn("Could not auto-load image:", e);