diff --git a/Auto-Image.js b/Auto-Image.js
index 83bd5b0..066a444 100644
--- a/Auto-Image.js
+++ b/Auto-Image.js
@@ -1775,6 +1775,111 @@
return r >= wt && g >= wt && b >= wt;
},
+ resolveColor(targetRgb, palette, exactMatch = false) {
+ if (!palette || palette.length === 0) return { id: null, rgb: targetRgb };
+
+ const cacheKey = `${targetRgb[0]},${targetRgb[1]},${targetRgb[2]}|${
+ state.colorMatchingAlgorithm
+ }|${state.enableChromaPenalty ? 'c' : 'nc'}|${state.chromaPenaltyWeight}|${exactMatch ? 'exact' : 'closest'}`;
+
+ if (colorCache.has(cacheKey)) return colorCache.get(cacheKey);
+
+ // точное совпадение
+ if (exactMatch) {
+ const match = palette.find(
+ (c) => c.rgb[0] === targetRgb[0] && c.rgb[1] === targetRgb[1] && c.rgb[2] === targetRgb[2]
+ );
+ const result = match ? { id: match.id, rgb: [...match.rgb] } : { id: null, rgb: targetRgb };
+ colorCache.set(cacheKey, result);
+ return result;
+ }
+
+ // проверка на белый через порог
+ const whiteThreshold = state.customWhiteThreshold || CONFIG.WHITE_THRESHOLD;
+ if (
+ targetRgb[0] >= whiteThreshold &&
+ targetRgb[1] >= whiteThreshold &&
+ targetRgb[2] >= whiteThreshold
+ ) {
+ const whiteEntry = palette.find(
+ (c) =>
+ c.rgb[0] >= whiteThreshold && c.rgb[1] >= whiteThreshold && c.rgb[2] >= whiteThreshold
+ );
+ if (whiteEntry) {
+ const result = { id: whiteEntry.id, rgb: [...whiteEntry.rgb] };
+ colorCache.set(cacheKey, result);
+ return result;
+ }
+ }
+
+ // поиск ближайшего цвета
+ let bestId = palette[0].id;
+ let bestRgb = [...palette[0].rgb];
+ let bestScore = Infinity;
+
+ if (state.colorMatchingAlgorithm === 'legacy') {
+ for (let i = 0; i < palette.length; i++) {
+ const c = palette[i];
+ const [r, g, b] = c.rgb;
+ const rmean = (r + targetRgb[0]) / 2;
+ const rdiff = r - targetRgb[0];
+ const gdiff = g - targetRgb[1];
+ const bdiff = b - targetRgb[2];
+ const dist = Math.sqrt(
+ (((512 + rmean) * rdiff * rdiff) >> 8) +
+ 4 * gdiff * gdiff +
+ (((767 - rmean) * bdiff * bdiff) >> 8)
+ );
+ if (dist < bestScore) {
+ bestScore = dist;
+ bestId = c.id;
+ bestRgb = [...c.rgb];
+ if (dist === 0) break;
+ }
+ }
+ } else {
+ const [Lt, at, bt] = Utils._lab(targetRgb[0], targetRgb[1], targetRgb[2]);
+ const targetChroma = Math.sqrt(at * at + bt * bt);
+ const penaltyWeight = state.enableChromaPenalty ? state.chromaPenaltyWeight || 0.15 : 0;
+
+ for (let i = 0; i < palette.length; i++) {
+ const c = palette[i];
+ const [r, g, b] = c.rgb;
+ const [L2, a2, b2] = Utils._lab(r, g, b);
+ const dL = Lt - L2,
+ da = at - a2,
+ db = bt - b2;
+ let dist = dL * dL + da * da + db * db;
+
+ if (penaltyWeight > 0 && targetChroma > 20) {
+ const candChroma = Math.sqrt(a2 * a2 + b2 * b2);
+ if (candChroma < targetChroma) {
+ const cd = targetChroma - candChroma;
+ dist += cd * cd * penaltyWeight;
+ }
+ }
+
+ if (dist < bestScore) {
+ bestScore = dist;
+ bestId = c.id;
+ bestRgb = [...c.rgb];
+ if (dist === 0) break;
+ }
+ }
+ }
+
+ const result = { id: bestId, rgb: bestRgb };
+ colorCache.set(cacheKey, result);
+
+ // ограничение кеша
+ if (colorCache.size > 15000) {
+ const firstKey = colorCache.keys().next().value;
+ colorCache.delete(firstKey);
+ }
+
+ return result;
+ },
+
createImageUploader: () =>
new Promise((resolve) => {
const input = document.createElement('input');
@@ -1827,7 +1932,7 @@
}),
extractAvailableColors: () => {
- const colorElements = document.querySelectorAll('[id^="color-"]');
+ const colorElements = document.querySelectorAll('.tooltip button[id^="color-"]');
// Separate available and unavailable colors
const availableColors = [];
@@ -1838,7 +1943,11 @@
if (id === 0) return; // Skip transparent color
const rgbStr = el.style.backgroundColor.match(/\d+/g);
- const rgb = rgbStr ? rgbStr.map(Number) : [0, 0, 0];
+ if (!rgbStr || rgbStr.length < 3) {
+ console.warn(`Skipping color element ${el.id} — cannot parse RGB`);
+ return;
+ }
+ const rgb = rgbStr.map(Number);
// Find color name from COLOR_MAP
const colorInfo = Object.values(CONFIG.COLOR_MAP).find((color) => color.id === id);
@@ -2114,54 +2223,83 @@
}
},
- saveProgress: () => {
- try {
- // Pack painted map if available
- let paintedMapPacked = null;
- if (state.paintedMap && state.imageData) {
- const data = Utils.packPaintedMapToBase64(
- state.paintedMap,
- state.imageData.width,
- state.imageData.height
- );
- if (data) {
- paintedMapPacked = {
+ buildPaintedMapPacked() {
+ if (state.paintedMap && state.imageData) {
+ const data = Utils.packPaintedMapToBase64(
+ state.paintedMap,
+ state.imageData.width,
+ state.imageData.height
+ );
+ if (data) {
+ return {
+ width: state.imageData.width,
+ height: state.imageData.height,
+ data: data,
+ };
+ }
+ }
+ return null;
+ },
+
+ buildProgressData() {
+ return {
+ timestamp: Date.now(),
+ version: '2.2',
+ state: {
+ totalPixels: state.totalPixels,
+ paintedPixels: state.paintedPixels,
+ lastPosition: state.lastPosition,
+ startPosition: state.startPosition,
+ region: state.region,
+ imageLoaded: state.imageLoaded,
+ colorsChecked: state.colorsChecked,
+ coordinateMode: state.coordinateMode,
+ coordinateDirection: state.coordinateDirection,
+ coordinateSnake: state.coordinateSnake,
+ blockWidth: state.blockWidth,
+ blockHeight: state.blockHeight,
+ availableColors: state.availableColors,
+ },
+ imageData: state.imageData
+ ? {
width: state.imageData.width,
height: state.imageData.height,
- data: data,
- };
- }
- }
+ pixels: Array.from(state.imageData.pixels),
+ totalPixels: state.imageData.totalPixels,
+ }
+ : null,
+ paintedMapPacked: Utils.buildPaintedMapPacked(),
+ };
+ },
- const progressData = {
- timestamp: Date.now(),
- version: '2.2',
- state: {
- totalPixels: state.totalPixels,
- paintedPixels: state.paintedPixels,
- lastPosition: state.lastPosition,
- startPosition: state.startPosition,
- region: state.region,
- imageLoaded: state.imageLoaded,
- colorsChecked: state.colorsChecked,
- availableColors: state.availableColors,
- // Добавляем настройки генерации координат
- coordinateMode: state.coordinateMode,
- coordinateDirection: state.coordinateDirection,
- coordinateSnake: state.coordinateSnake,
- blockWidth: state.blockWidth,
- blockHeight: state.blockHeight,
- },
- imageData: state.imageData
- ? {
- width: state.imageData.width,
- height: state.imageData.height,
- pixels: Array.from(state.imageData.pixels),
- totalPixels: state.imageData.totalPixels,
- }
- : null,
- paintedMapPacked: paintedMapPacked,
- };
+ migrateProgress(saved) {
+ if (!saved) return null;
+
+ let data = saved;
+ const ver = data.version;
+
+ // если версия не указана или <= 1.x → сначала в v2
+ if (!ver || ver === '1' || ver === '1.0' || ver === '1.1') {
+ data = Utils.migrateProgressToV2(data);
+ }
+
+ // если всё ещё старее v2.1 → мигрируем в 2.1
+ if (data.version === '2' || data.version === '2.0') {
+ data = Utils.migrateProgressToV21(data);
+ }
+
+ // если всё ещё старее v2.2 → мигрируем в 2.2
+ if (data.version === '2.1') {
+ data = Utils.migrateProgressToV22(data);
+ }
+
+ // теперь точно последняя версия
+ return data;
+ },
+
+ saveProgress: () => {
+ try {
+ const progressData = Utils.buildProgressData(state);
localStorage.setItem('wplace-bot-progress', JSON.stringify(progressData));
return true;
@@ -2176,27 +2314,14 @@
const saved = localStorage.getItem('wplace-bot-progress');
if (!saved) return null;
let data = JSON.parse(saved);
- const ver = data.version;
- let migrated = data;
-
- // Миграция для новых версий
- if (ver === '2.2') {
- // already latest
- } else if (ver === '2.1') {
- migrated = Utils.migrateProgressToV22(data);
- } else if (ver === '2' || ver === '2.0') {
- migrated = Utils.migrateProgressToV22(Utils.migrateProgressToV21(data));
- } else {
- migrated = Utils.migrateProgressToV22(Utils.migrateProgressToV21(data));
- }
+ const migrated = Utils.migrateProgress(data);
if (migrated && migrated !== data) {
try {
localStorage.setItem('wplace-bot-progress', JSON.stringify(migrated));
} catch {}
- data = migrated;
}
- return data;
+ return migrated;
} catch (error) {
console.error('Error loading progress:', error);
return null;
@@ -2289,47 +2414,7 @@
saveProgressToFile: () => {
try {
- // Pack painted map if available
- let paintedMapPacked = null;
- if (state.paintedMap && state.imageData) {
- const data = Utils.packPaintedMapToBase64(
- state.paintedMap,
- state.imageData.width,
- state.imageData.height
- );
- if (data) {
- paintedMapPacked = {
- width: state.imageData.width,
- height: state.imageData.height,
- data: data,
- };
- }
- }
-
- const progressData = {
- timestamp: Date.now(),
- version: '2.1',
- state: {
- totalPixels: state.totalPixels,
- paintedPixels: state.paintedPixels,
- lastPosition: state.lastPosition,
- startPosition: state.startPosition,
- region: state.region,
- imageLoaded: state.imageLoaded,
- colorsChecked: state.colorsChecked,
- availableColors: state.availableColors,
- },
- imageData: state.imageData
- ? {
- width: state.imageData.width,
- height: state.imageData.height,
- pixels: Array.from(state.imageData.pixels),
- totalPixels: state.imageData.totalPixels,
- }
- : null,
- paintedMapPacked: paintedMapPacked,
- };
-
+ const progressData = Utils.buildProgressData();
const filename = `wplace-bot-progress-${new Date()
.toISOString()
.slice(0, 19)
@@ -2348,14 +2433,8 @@
if (!data || !data.state) {
throw new Error('Invalid file format');
}
- const ver = data.version;
- let migrated = data;
- if (ver === '2.1') {
- } else if (ver === '2' || ver === '2.0') {
- migrated = Utils.migrateProgressToV21(data) || data;
- } else {
- migrated = Utils.migrateProgressToV21(data) || data;
- }
+ const migrated = Utils.migrateProgress(data);
+
const success = Utils.restoreProgress(migrated);
return success;
} catch (error) {
@@ -2642,90 +2721,9 @@
// COLOR MATCHING FUNCTION - Optimized with caching
const colorCache = new Map();
- function findClosestColor(targetRgb, availableColors) {
- if (!availableColors || availableColors.length === 0) return 1;
- const cacheKey = `${targetRgb[0]},${targetRgb[1]},${targetRgb[2]}|${
- state.colorMatchingAlgorithm
- }|${state.enableChromaPenalty ? 'c' : 'nc'}|${state.chromaPenaltyWeight}`;
- if (colorCache.has(cacheKey)) return colorCache.get(cacheKey);
-
- const whiteThreshold = state.customWhiteThreshold || CONFIG.WHITE_THRESHOLD;
- if (
- targetRgb[0] >= whiteThreshold &&
- targetRgb[1] >= whiteThreshold &&
- targetRgb[2] >= whiteThreshold
- ) {
- const whiteEntry = availableColors.find(
- (c) =>
- c.rgb[0] >= whiteThreshold && c.rgb[1] >= whiteThreshold && c.rgb[2] >= whiteThreshold
- );
- if (whiteEntry) {
- colorCache.set(cacheKey, whiteEntry.id);
- return whiteEntry.id;
- }
- }
-
- let bestId = availableColors[0].id;
- let bestScore = Infinity;
-
- if (state.colorMatchingAlgorithm === 'legacy') {
- for (let i = 0; i < availableColors.length; i++) {
- const c = availableColors[i];
- const [r, g, b] = c.rgb;
- const rmean = (r + targetRgb[0]) / 2;
- const rdiff = r - targetRgb[0];
- const gdiff = g - targetRgb[1];
- const bdiff = b - targetRgb[2];
- const dist = Math.sqrt(
- (((512 + rmean) * rdiff * rdiff) >> 8) +
- 4 * gdiff * gdiff +
- (((767 - rmean) * bdiff * bdiff) >> 8)
- );
- if (dist < bestScore) {
- bestScore = dist;
- bestId = c.id;
- if (dist === 0) break;
- }
- }
- } else {
- // lab
- const [Lt, at, bt] = Utils._lab(targetRgb[0], targetRgb[1], targetRgb[2]);
- const targetChroma = Math.sqrt(at * at + bt * bt);
- const penaltyWeight = state.enableChromaPenalty ? state.chromaPenaltyWeight || 0.15 : 0;
- for (let i = 0; i < availableColors.length; i++) {
- const c = availableColors[i];
- const [r, g, b] = c.rgb;
- const [L2, a2, b2] = Utils._lab(r, g, b);
- const dL = Lt - L2,
- da = at - a2,
- db = bt - b2;
- let dist = dL * dL + da * da + db * db;
- if (penaltyWeight > 0 && targetChroma > 20) {
- const candChroma = Math.sqrt(a2 * a2 + b2 * b2);
- if (candChroma < targetChroma) {
- const cd = targetChroma - candChroma;
- dist += cd * cd * penaltyWeight;
- }
- }
- if (dist < bestScore) {
- bestScore = dist;
- bestId = c.id;
- if (dist === 0) break;
- }
- }
- }
-
- colorCache.set(cacheKey, bestId);
- if (colorCache.size > 15000) {
- const firstKey = colorCache.keys().next().value;
- colorCache.delete(firstKey);
- }
- return bestId;
- }
-
// UI UPDATE FUNCTIONS (declared early to avoid reference errors)
let updateUI = () => {};
- let updateStats = () => {};
+ let updateStats = (isManualRefresh) => {};
let updateDataButtons = () => {};
function updateActiveColorPalette() {
@@ -3271,6 +3269,7 @@
}
`;
+ // noinspection CssInvalidFunction
settingsContainer.innerHTML = `
-
+