From 6b39d8c58076cdbad4ea712d2f4b7809f97c1963 Mon Sep 17 00:00:00 2001
From: jamz-uk <222520099+jamz-uk@users.noreply.github.com>
Date: Fri, 22 Aug 2025 20:24:50 +0100
Subject: [PATCH] logic
---
Auto-Image.js | 221 ++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 187 insertions(+), 34 deletions(-)
diff --git a/Auto-Image.js b/Auto-Image.js
index f34a371..78d33d8 100644
--- a/Auto-Image.js
+++ b/Auto-Image.js
@@ -4392,8 +4392,24 @@
@@ -5076,11 +5092,13 @@
}
baseCtx.clearRect(0,0,newWidth,newHeight);
baseCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight);
+ // Draw mask on overlay canvas only
+ maskCtx.clearRect(0,0,maskCanvas.width,maskCanvas.height);
if (state.resizeIgnoreMask) {
- const imgData = baseCtx.getImageData(0,0,newWidth,newHeight);
- const m = state.resizeIgnoreMask;
- for (let i=0;i { onWidthInput(); schedulePreview(); });
- heightSlider.addEventListener("input", () => { onHeightInput(); schedulePreview(); });
+ widthSlider.addEventListener("input", () => { onWidthInput(); schedulePreview(); });
+ heightSlider.addEventListener("input", () => { onHeightInput(); schedulePreview(); });
- // Click/drag to toggle ignored pixels
+ // Mask painting UX: brush size, modes, row/column fills, and precise coords
let draggingMask = false;
- const pointToggle = (clientX, clientY) => {
+ let lastPaintX = -1, lastPaintY = -1;
+ let brushSize = 1;
+ let maskMode = 'toggle'; // 'ignore' | 'unignore' | 'toggle'
+ const brushEl = resizeContainer.querySelector('#maskBrushSize');
+ const brushValEl = resizeContainer.querySelector('#maskBrushSizeValue');
+ const btnIgnore = resizeContainer.querySelector('#maskModeIgnore');
+ const btnUnignore = resizeContainer.querySelector('#maskModeUnignore');
+ const btnToggle = resizeContainer.querySelector('#maskModeToggle');
+ const clearIgnoredBtnEl = resizeContainer.querySelector('#clearIgnoredBtn');
+ const invertMaskBtn = resizeContainer.querySelector('#invertMaskBtn');
+
+ const setMode = (mode) => {
+ maskMode = mode;
+ btnIgnore.classList.toggle('wplace-btn-primary', mode === 'ignore');
+ btnUnignore.classList.toggle('wplace-btn-primary', mode === 'unignore');
+ btnToggle.classList.toggle('wplace-btn-primary', mode === 'toggle');
+ };
+ if (brushEl && brushValEl) {
+ brushEl.addEventListener('input', () => { brushSize = parseInt(brushEl.value, 10) || 1; brushValEl.textContent = brushSize; });
+ brushValEl.textContent = brushEl.value;
+ brushSize = parseInt(brushEl.value, 10) || 1;
+ }
+ if (btnIgnore) btnIgnore.addEventListener('click', () => setMode('ignore'));
+ if (btnUnignore) btnUnignore.addEventListener('click', () => setMode('unignore'));
+ if (btnToggle) btnToggle.addEventListener('click', () => setMode('toggle'));
+
+ const mapClientToPixel = (clientX, clientY) => {
+ // Compute without rounding until final step to avoid drift at higher zoom
const rect = baseCanvas.getBoundingClientRect();
- const x = Math.floor((clientX - rect.left) / parseFloat(zoomSlider.value));
- const y = Math.floor((clientY - rect.top) / parseFloat(zoomSlider.value));
- const w = baseCanvas.width, h = baseCanvas.height;
- if (x < 0 || y < 0 || x >= w || y >= h) return;
- const idx = y * w + x;
+ const scaleX = rect.width / baseCanvas.width;
+ const scaleY = rect.height / baseCanvas.height;
+ const dx = (clientX - rect.left) / scaleX;
+ const dy = (clientY - rect.top) / scaleY;
+ const x = Math.floor(dx);
+ const y = Math.floor(dy);
+ return { x, y };
+ };
+
+ const ensureMask = (w, h) => {
if (!state.resizeIgnoreMask || state.resizeIgnoreMask.length !== w * h) {
state.resizeIgnoreMask = new Uint8Array(w * h);
}
- state.resizeIgnoreMask[idx] = state.resizeIgnoreMask[idx] ? 0 : 1;
- // quick visual hint by updating a pixel in mask canvas
- const color = state.resizeIgnoreMask[idx] ? 'rgba(255,0,0,0.6)' : 'rgba(0,0,0,0)';
- if (state.resizeIgnoreMask[idx]) {
- maskCtx.fillStyle = color; maskCtx.fillRect(x, y, 1, 1);
- } else {
- maskCtx.clearRect(x, y, 1, 1);
+ };
+
+ const paintCircle = (cx, cy, radius, value) => {
+ const w = baseCanvas.width, h = baseCanvas.height;
+ ensureMask(w, h);
+ const r2 = radius * radius;
+ for (let yy = cy - radius; yy <= cy + radius; yy++) {
+ if (yy < 0 || yy >= h) continue;
+ for (let xx = cx - radius; xx <= cx + radius; xx++) {
+ if (xx < 0 || xx >= w) continue;
+ const dx = xx - cx, dy = yy - cy;
+ if (dx * dx + dy * dy <= r2) {
+ const idx = yy * w + xx;
+ if (maskMode === 'toggle') {
+ state.resizeIgnoreMask[idx] = state.resizeIgnoreMask[idx] ? 0 : 1;
+ } else if (maskMode === 'ignore') {
+ state.resizeIgnoreMask[idx] = 1;
+ } else {
+ state.resizeIgnoreMask[idx] = 0;
+ }
+ }
+ }
}
};
- maskCanvas.addEventListener('mousedown', (e) => { draggingMask = true; pointToggle(e.clientX, e.clientY); _updateResizePreview(); });
- window.addEventListener('mousemove', (e) => { if (draggingMask) { pointToggle(e.clientX, e.clientY); } });
- window.addEventListener('mouseup', () => { if (draggingMask) { draggingMask = false; saveBotSettings(); } });
- const clearIgnoredBtnEl = resizeContainer.querySelector('#clearIgnoredBtn');
- if (clearIgnoredBtnEl) clearIgnoredBtnEl.addEventListener('click', () => { if (state.resizeIgnoreMask) state.resizeIgnoreMask.fill(0); maskCtx.clearRect(0,0,maskCanvas.width,maskCanvas.height); _updateResizePreview(); saveBotSettings(); });
+
+ const paintRow = (y, value) => {
+ const w = baseCanvas.width, h = baseCanvas.height;
+ ensureMask(w, h);
+ if (y < 0 || y >= h) return;
+ for (let x = 0; x < w; x++) {
+ const idx = y * w + x;
+ if (maskMode === 'toggle') {
+ state.resizeIgnoreMask[idx] = state.resizeIgnoreMask[idx] ? 0 : 1;
+ } else if (maskMode === 'ignore') {
+ state.resizeIgnoreMask[idx] = 1;
+ } else {
+ state.resizeIgnoreMask[idx] = 0;
+ }
+ }
+ };
+
+ const paintColumn = (x, value) => {
+ const w = baseCanvas.width, h = baseCanvas.height;
+ ensureMask(w, h);
+ if (x < 0 || x >= w) return;
+ for (let y = 0; y < h; y++) {
+ const idx = y * w + x;
+ if (maskMode === 'toggle') {
+ state.resizeIgnoreMask[idx] = state.resizeIgnoreMask[idx] ? 0 : 1;
+ } else if (maskMode === 'ignore') {
+ state.resizeIgnoreMask[idx] = 1;
+ } else {
+ state.resizeIgnoreMask[idx] = 0;
+ }
+ }
+ };
+
+ const redrawMaskOverlay = () => {
+ const w = baseCanvas.width, h = baseCanvas.height;
+ maskCanvas.width = w; maskCanvas.height = h;
+ maskCtx.clearRect(0, 0, w, h);
+ if (!state.resizeIgnoreMask) return;
+ // Draw ignored pixels as semi-transparent red squares
+ const img = maskCtx.createImageData(w, h);
+ const md = img.data;
+ for (let i = 0; i < state.resizeIgnoreMask.length; i++) {
+ if (state.resizeIgnoreMask[i]) {
+ const p = i * 4;
+ md[p] = 255; md[p + 1] = 0; md[p + 2] = 0; md[p + 3] = 150;
+ }
+ }
+ maskCtx.putImageData(img, 0, 0);
+ };
+
+ const handlePaint = (e) => {
+ const { x, y } = mapClientToPixel(e.clientX, e.clientY);
+ const w = baseCanvas.width, h = baseCanvas.height;
+ if (x < 0 || y < 0 || x >= w || y >= h) return;
+ const radius = Math.max(1, Math.floor(brushSize / 2));
+ if (e.shiftKey) {
+ paintRow(y);
+ } else if (e.altKey) {
+ paintColumn(x);
+ } else {
+ paintCircle(x, y, radius);
+ }
+ lastPaintX = x; lastPaintY = y;
+ redrawMaskOverlay();
+ };
+
+ maskCanvas.addEventListener('mousedown', (e) => { draggingMask = true; handlePaint(e); });
+ window.addEventListener('mousemove', (e) => { if (draggingMask) handlePaint(e); });
+ window.addEventListener('mouseup', () => { if (draggingMask) { draggingMask = false; saveBotSettings(); }});
+
+ if (clearIgnoredBtnEl) clearIgnoredBtnEl.addEventListener('click', () => {
+ if (state.resizeIgnoreMask) state.resizeIgnoreMask.fill(0);
+ redrawMaskOverlay();
+ _updateResizePreview();
+ saveBotSettings();
+ });
+
+ if (invertMaskBtn) invertMaskBtn.addEventListener('click', () => {
+ if (!state.resizeIgnoreMask) return;
+ for (let i = 0; i < state.resizeIgnoreMask.length; i++) state.resizeIgnoreMask[i] = state.resizeIgnoreMask[i] ? 0 : 1;
+ redrawMaskOverlay();
+ _updateResizePreview();
+ saveBotSettings();
+ });
confirmResize.onclick = async () => {
const newWidth = parseInt(widthSlider.value, 10);
@@ -5391,9 +5537,16 @@
downloadPreviewBtn.onclick = () => {
try {
+ const w = baseCanvas.width, h = baseCanvas.height;
+ const out = document.createElement('canvas');
+ out.width = w; out.height = h;
+ const octx = out.getContext('2d');
+ octx.imageSmoothingEnabled = false;
+ octx.drawImage(baseCanvas, 0, 0);
+ octx.drawImage(maskCanvas, 0, 0);
const link = document.createElement('a');
link.download = 'wplace-preview.png';
- link.href = baseCanvas.toDataURL();
+ link.href = out.toDataURL();
link.click();
} catch (e) { console.warn('Failed to download preview:', e); }
};