Added invalid palette colors as an "other" option in filtering

This commit is contained in:
SwingTheVine 2025-08-19 23:59:52 -04:00
parent 53fb918dec
commit ed15a15577
8 changed files with 136 additions and 65 deletions

View file

@ -1 +1 @@
#bm-s,#bm-s-telemetry{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000;transition:all .3s ease,transform 0s;max-width:300px;width:auto;will-change:transform;backface-visibility:hidden;-webkit-backface-visibility:hidden;transform-style:preserve-3d;-webkit-transform-style:preserve-3d}#bm-8,#bm-s hr,#bm-s-telemetry hr,#bm-7,#bm-3{transition:opacity .2s ease,height .2s ease}div#bm-s,div#bm-s-telemetry{font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-r,#bm-r-telemetry{margin-bottom:.5em;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:1em}#bm-r.dragging,#bm-r-telemetry.dragging{cursor:grabbing}#bm-s:has(#bm-r.dragging),#bm-s-telemetry:has(#bm-r-telemetry.dragging){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}#bm-r.dragging,#bm-r-telemetry.dragging{pointer-events:auto}#bm-c,#bm-c-telemetry{margin-bottom:.5em}#bm-c[style*="text-align: center"],#bm-c-telemetry[style*="text-align: center"]{display:flex;flex-direction:column;align-items:center;justify-content:center}#bm-s[style*="padding: 5px"],#bm-s-telemetry[style*="padding: 5px"]{width:auto!important;max-width:300px;min-width:200px}#bm-s img{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle;transition:opacity .2s ease}#bm-c[style*="text-align: center"] img{display:block;margin:0 auto}#bm-r,#bm-r-telemetry{transition:margin-bottom .2s ease}#bm-s h1,#bm-s-telemetry h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}#bm-7 input[type=checkbox]{vertical-align:middle;margin-right:.5ch}#bm-7 label{margin-right:.5ch}.bm-v{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}#bm-i{vertical-align:middle}#bm-i svg{width:50%;margin:0 auto;fill:#111}div:has(>#bm-button-teleport){display:flex;gap:.5ch}#bm-button-favorite svg,#bm-button-template svg{height:1em;margin:2px auto 0;text-align:center;line-height:1em;vertical-align:bottom}#bm-d input[type=number]{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}#bm-d input[type=number]::-webkit-outer-spin-button,#bm-d input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}#bm-2{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-5)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#bm-5,input[type=file][id*=template]{display:none!important;visibility:hidden!important;position:absolute!important;left:-9999px!important;top:-9999px!important;width:0!important;height:0!important;opacity:0!important;z-index:-9999!important;pointer-events:none!important}#bm-g{font-size:small;background-color:#0003;padding:0 .5ch;height:3.75em;width:100%}#bm-3{display:flex;justify-content:space-between}#bm-s small{font-size:x-small;color:#d3d3d3}#bm-8,#bm-7,#bm-d,#bm-2,div:has(>#bm-5),#bm-g{margin-top:.5em}#bm-s button,#bm-s-telemetry button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}#bm-s button:hover,#bm-s button:focus-visible,#bm-s-telemetry button:hover,#bm-s-telemetry button:focus-visible{background-color:#1061e5}#bm-s button:active,#bm-s-telemetry button:active #bm-s button:disabled,#bm-s-telemetry button:disabled{background-color:#2e97ff}#bm-s button:disabled,#bm-s-telemetry button:disabled{text-decoration:line-through}
#bm-A,#bm-d{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000;transition:all .3s ease,transform 0s;max-width:300px;width:auto;will-change:transform;backface-visibility:hidden;-webkit-backface-visibility:hidden;transform-style:preserve-3d;-webkit-transform-style:preserve-3d}#bm-f,#bm-A hr,#bm-d hr,#bm-c,#bm-6{transition:opacity .2s ease,height .2s ease}div#bm-A,div#bm-d{font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-z,#bm-z-telemetry{margin-bottom:.5em;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:1em}#bm-z.dragging,#bm-z-telemetry.dragging{cursor:grabbing}#bm-A:has(#bm-z.dragging),#bm-d:has(#bm-z-telemetry.dragging){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}#bm-z.dragging,#bm-z-telemetry.dragging{pointer-events:auto}#bm-j,#bm-1{margin-bottom:.5em}#bm-j[style*="text-align: center"],#bm-1[style*="text-align: center"]{display:flex;flex-direction:column;align-items:center;justify-content:center}#bm-A[style*="padding: 5px"],#bm-d[style*="padding: 5px"]{width:auto!important;max-width:300px;min-width:200px}#bm-A img{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle;transition:opacity .2s ease}#bm-j[style*="text-align: center"] img{display:block;margin:0 auto}#bm-z,#bm-z-telemetry{transition:margin-bottom .2s ease}#bm-A h1,#bm-d h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}#bm-c input[type=checkbox]{vertical-align:middle;margin-right:.5ch}#bm-c label{margin-right:.5ch}.bm-D{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}#bm-q{vertical-align:middle}#bm-q svg{width:50%;margin:0 auto;fill:#111}div:has(>#bm-button-teleport){display:flex;gap:.5ch}#bm-button-favorite svg,#bm-button-template svg{height:1em;margin:2px auto 0;text-align:center;line-height:1em;vertical-align:bottom}#bm-k input[type=number]{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}#bm-k input[type=number]::-webkit-outer-spin-button,#bm-k input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}#bm-4{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-a)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#bm-a,input[type=file][id*=template]{display:none!important;visibility:hidden!important;position:absolute!important;left:-9999px!important;top:-9999px!important;width:0!important;height:0!important;opacity:0!important;z-index:-9999!important;pointer-events:none!important}#bm-o{font-size:small;background-color:#0003;padding:0 .5ch;height:3.75em;width:100%}#bm-6{display:flex;justify-content:space-between}#bm-A small{font-size:x-small;color:#d3d3d3}#bm-f,#bm-c,#bm-k,#bm-4,div:has(>#bm-a),#bm-o{margin-top:.5em}#bm-A button,#bm-d button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}#bm-A button:hover,#bm-A button:focus-visible,#bm-d button:hover,#bm-d button:focus-visible{background-color:#1061e5}#bm-A button:active,#bm-d button:active #bm-A button:disabled,#bm-d button:disabled{background-color:#2e97ff}#bm-A button:disabled,#bm-d button:disabled{text-decoration:line-through}

File diff suppressed because one or more lines are too long

View file

@ -49,7 +49,7 @@
<a href="https://discord.gg/tpeBPy46hf" target="_blank" rel="noopener noreferrer"><img alt="Contact Me" src="https://img.shields.io/badge/Contact_Me-gray?style=flat&logo=Discord&logoColor=white&logoSize=auto&labelColor=cornflowerblue"></a>
<a href="https://bluemarble.camilledaguin.fr/" target="_blank" rel="noopener noreferrer"><img alt="Blue Marble Website" src="https://img.shields.io/badge/Blue_Marble_Website-Camille_Daguin-blue?style=flat&logo=globe&logoColor=white"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="WakaTime" src="https://img.shields.io/badge/Coding_Time-111hrs_12mins-blue?style=flat&logo=wakatime&logoColor=black&logoSize=auto&labelColor=white"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-494-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-508-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Lines of Code" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=code"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Comments" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=comments"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Compression" src="https://img.shields.io/badge/Compression-69.52%25-blue"></a>

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "wplace-bluemarble",
"version": "0.82.54",
"version": "0.84.13",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wplace-bluemarble",
"version": "0.82.54",
"version": "0.84.13",
"devDependencies": {
"esbuild": "^0.25.0",
"jsdoc": "^4.0.4",

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.84.0",
"version": "0.84.13",
"type": "module",
"homepage": "https://bluemarble.camilledaguin.fr/",
"repository": {

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.84.0
// @version 0.84.13
// @description A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
// @author SwingTheVine
// @license MPL-2.0

View file

@ -46,28 +46,42 @@ export default class Template {
this.storageKey = null; // Key used inside templatesJSON to persist settings
// Build allowed color set from site palette (exclude special Transparent entry by name)
// Creates a Set of Wplace palette colors excluding "transparent"
const allowed = Array.isArray(colorpalette) ? colorpalette : [];
this.allowedColorsSet = new Set(
allowed
.filter(c => (c?.name || '').toLowerCase() !== 'transparent' && Array.isArray(c?.rgb))
.map(c => `${c.rgb[0]},${c.rgb[1]},${c.rgb[2]}`)
.filter(color => (color?.name || '').toLowerCase() !== 'transparent' && Array.isArray(color?.rgb))
.map(color => `${color.rgb[0]},${color.rgb[1]},${color.rgb[2]}`)
);
// Ensure template #deface marker is treated as allowed (maps to Transparent color)
const defaceKey = '222,250,206';
this.allowedColorsSet.add(defaceKey);
const keyOther = 'other';
this.allowedColorsSet.add(keyOther); // Special "other" key for non-palette colors
// Map rgb-> {id, premium}
this.rgbToMeta = new Map(
allowed
.filter(c => Array.isArray(c?.rgb))
.map(c => [ `${c.rgb[0]},${c.rgb[1]},${c.rgb[2]}`, { id: c.id, premium: !!c.premium, name: c.name } ])
.filter(color => Array.isArray(color?.rgb))
.map(color => [ `${color.rgb[0]},${color.rgb[1]},${color.rgb[2]}`, { id: color.id, premium: !!color.premium, name: color.name } ])
);
// Map #deface to Transparent meta for UI naming and ID continuity
try {
const transparent = allowed.find(c => (c?.name || '').toLowerCase() === 'transparent');
const transparent = allowed.find(color => (color?.name || '').toLowerCase() === 'transparent');
if (transparent && Array.isArray(transparent.rgb)) {
this.rgbToMeta.set(defaceKey, { id: transparent.id, premium: !!transparent.premium, name: transparent.name });
}
} catch (_) {}
} catch (ignored) {}
// Map other key to Other meta for UI naming and ID continuity
try {
this.rgbToMeta.set(keyOther, { id: 'other', premium: false, name: 'Other' });
} catch (ignored) {}
console.log('Allowed colors for template:', this.allowedColorsSet);
}
/** Creates chunks of the template for each tile.
@ -112,9 +126,9 @@ export default class Template {
const b = inspectData[idx + 2];
const a = inspectData[idx + 3];
if (a === 0) { continue; } // Ignored transparent pixel
const key = `${r},${g},${b}`;
if (r === 222 && g === 250 && b === 206) { deface++; }
if (!this.allowedColorsSet.has(key)) { continue; } // Skip non-palette colors (but #deface added to allowed)
const key = this.allowedColorsSet.has(`${r},${g},${b}`) ? `${r},${g},${b}` : 'other';
//if (!this.allowedColorsSet.has(key)) { continue; } // Skip non-palette colors (but #deface added to allowed)
required++;
paletteMap.set(key, (paletteMap.get(key) || 0) + 1);
}
@ -228,7 +242,7 @@ export default class Template {
const g = imageData.data[pixelIndex + 1];
const b = imageData.data[pixelIndex + 2];
if (!this.allowedColorsSet.has(`${r},${g},${b}`)) {
imageData.data[pixelIndex + 3] = 0; // hide non-palette colors
//imageData.data[pixelIndex + 3] = 0; // hide non-palette colors
}
}
}

View file

@ -314,33 +314,46 @@ export default class TemplateManager {
// honoring color enable/disable from the active template's palette
if (tilePixels) {
try {
const tempW = template.bitmap.width;
const tempH = template.bitmap.height;
const tempCanvas = new OffscreenCanvas(tempW, tempH);
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
tempCtx.imageSmoothingEnabled = false;
tempCtx.clearRect(0, 0, tempW, tempH);
tempCtx.drawImage(template.bitmap, 0, 0);
const tImg = tempCtx.getImageData(0, 0, tempW, tempH);
const tData = tImg.data;
const tempWidth = template.bitmap.width;
const tempHeight = template.bitmap.height;
const tempCanvas = new OffscreenCanvas(tempWidth, tempHeight);
const tempContext = tempCanvas.getContext('2d', { willReadFrequently: true });
tempContext.imageSmoothingEnabled = false;
tempContext.clearRect(0, 0, tempWidth, tempHeight);
tempContext.drawImage(template.bitmap, 0, 0);
const tImg = tempContext.getImageData(0, 0, tempWidth, tempHeight);
const tData = tImg.data; // Tile Data, Template Data, or Temp Data????
const offsetX = Number(template.pixelCoords[0]) * this.drawMult;
const offsetY = Number(template.pixelCoords[1]) * this.drawMult;
for (let y = 0; y < tempH; y++) {
for (let x = 0; x < tempW; x++) {
// Loops over all pixels in the template
// Assigns each pixel a color (if center pixel)
for (let y = 0; y < tempHeight; y++) {
for (let x = 0; x < tempWidth; x++) {
// Purpose: Count which pixels are painted correctly???
// Only evaluate the center pixel of each shread block
// Skip if not the center pixel of the shread block
if ((x % this.drawMult) !== 1 || (y % this.drawMult) !== 1) { continue; }
const gx = x + offsetX;
const gy = y + offsetY;
// IF the pixel is out of bounds of the template, OR if the pixel is outside of the tile, then skip the pixel
if (gx < 0 || gy < 0 || gx >= drawSize || gy >= drawSize) { continue; }
const tIdx = (y * tempW + x) * 4;
const tr = tData[tIdx];
const tg = tData[tIdx + 1];
const tb = tData[tIdx + 2];
const ta = tData[tIdx + 3];
const templatePixelCenter = (y * tempWidth + x) * 4; // Shread block center pixel
const templatePixelCenterRed = tData[templatePixelCenter]; // Shread block's center pixel's RED value
const templatePixelCenterGreen = tData[templatePixelCenter + 1]; // Shread block's center pixel's GREEN value
const templatePixelCenterBlue = tData[templatePixelCenter + 2]; // Shread block's center pixel's BLUE value
const templatePixelCenterAlpha = tData[templatePixelCenter + 3]; // Shread block's center pixel's ALPHA value
// Possibly needs to be removed
// Handle template transparent pixel (alpha < 64): wrong if board has any site palette color here
if (ta < 64) {
// If the alpha of the center pixel is less than 64...
if (templatePixelCenterAlpha < 64) {
try {
const activeTemplate = this.templatesArray?.[0];
const tileIdx = (gy * drawSize + gx) * 4;
@ -348,85 +361,128 @@ export default class TemplateManager {
const pg = tilePixels[tileIdx + 1];
const pb = tilePixels[tileIdx + 2];
const pa = tilePixels[tileIdx + 3];
const key = `${pr},${pg},${pb}`;
const key = activeTemplate.allowedColorsSet.has(`${pr},${pg},${pb}`) ? `${pr},${pg},${pb}` : 'other';
const isSiteColor = activeTemplate?.allowedColorsSet ? activeTemplate.allowedColorsSet.has(key) : false;
// IF the alpha of the center pixel that is placed on the canvas is greater than or equal to 64, AND the pixel is a Wplace palette color, then it is incorrect.
if (pa >= 64 && isSiteColor) {
wrongCount++;
}
} catch (_) {}
continue;
} catch (ignored) {}
continue; // Continue to the next pixel
}
// Treat #deface as Transparent palette color (required and paintable)
// Ignore non-palette colors (match against allowed set when available)
try {
const activeTemplate = this.templatesArray?.[0];
if (activeTemplate?.allowedColorsSet && !activeTemplate.allowedColorsSet.has(`${tr},${tg},${tb}`)) {
continue;
}
} catch (_) {}
// Ignore non-palette colors (match against allowed set when available) for counting required template pixels
// try {
// const activeTemplate = this.templatesArray?.[0]; // Get the first template
// // IF the stored palette data exists, AND the pixel is not in the allowed palette
// if (activeTemplate?.allowedColorsSet && !activeTemplate.allowedColorsSet.has(`${templatePixelCenterRed},${templatePixelCenterGreen},${templatePixelCenterBlue}`)) {
// continue; // Skip this pixel if it is not in the allowed palette
// }
// } catch (ignored) {}
requiredCount++;
// Strict center-pixel matching. Treat transparent tile pixels as unpainted (not wrong)
const tileIdx = (gy * drawSize + gx) * 4;
const pr = tilePixels[tileIdx];
const pg = tilePixels[tileIdx + 1];
const pb = tilePixels[tileIdx + 2];
const pa = tilePixels[tileIdx + 3];
const realPixelCenter = (gy * drawSize + gx) * 4;
const realPixelRed = tilePixels[realPixelCenter];
const realPixelCenterGreen = tilePixels[realPixelCenter + 1];
const realPixelCenterBlue = tilePixels[realPixelCenter + 2];
const realPixelCenterAlpha = tilePixels[realPixelCenter + 3];
if (pa < 64) {
// IF the alpha of the pixel is less than 64...
if (realPixelCenterAlpha < 64) {
// Unpainted -> neither painted nor wrong
} else if (pr === tr && pg === tg && pb === tb) {
paintedCount++;
// ELSE IF the pixel matches the template center pixel color
} else if (realPixelRed === templatePixelCenterRed && realPixelCenterGreen === templatePixelCenterGreen && realPixelCenterBlue === templatePixelCenterBlue) {
paintedCount++; // ...the pixel is painted correctly
} else {
wrongCount++;
wrongCount++; // ...the pixel is NOT painted correctly
}
}
}
} catch (e) {
console.warn('Failed to compute per-tile painted/wrong stats:', e);
} catch (exception) {
console.warn('Failed to compute per-tile painted/wrong stats:', exception);
}
}
// Draw the template overlay for visual guidance, honoring color filter
try {
const activeTemplate = this.templatesArray?.[0];
const palette = activeTemplate?.colorPalette || {};
const hasDisabled = Object.values(palette).some(v => v?.enabled === false);
const activeTemplate = this.templatesArray?.[0]; // Get the first template
const palette = activeTemplate?.colorPalette || {}; // Obtain the color palette of the template
const hasDisabled = Object.values(palette).some(v => v?.enabled === false); // Check if any color is disabled
// If none of the template colors are disabled, then draw the image normally
if (!hasDisabled) {
context.drawImage(template.bitmap, Number(template.pixelCoords[0]) * this.drawMult, Number(template.pixelCoords[1]) * this.drawMult);
} else {
// ELSE we need to apply the color filter
console.log('Applying color filter...');
const tempW = template.bitmap.width;
const tempH = template.bitmap.height;
const filterCanvas = new OffscreenCanvas(tempW, tempH);
const filterCtx = filterCanvas.getContext('2d', { willReadFrequently: true });
filterCtx.imageSmoothingEnabled = false;
filterCtx.imageSmoothingEnabled = false; // Nearest neighbor
filterCtx.clearRect(0, 0, tempW, tempH);
filterCtx.drawImage(template.bitmap, 0, 0);
const img = filterCtx.getImageData(0, 0, tempW, tempH);
const data = img.data;
// For every pixel...
for (let y = 0; y < tempH; y++) {
for (let x = 0; x < tempW; x++) {
// If this pixel is NOT the center pixel, then skip the pixel
if ((x % this.drawMult) !== 1 || (y % this.drawMult) !== 1) { continue; }
const idx = (y * tempW + x) * 4;
const r = data[idx];
const g = data[idx + 1];
const b = data[idx + 2];
const a = data[idx + 3];
if (a < 1) { continue; }
const key = `${r},${g},${b}`;
let key = activeTemplate.allowedColorsSet.has(`${r},${g},${b}`) ? `${r},${g},${b}` : 'other';
// Hide if color is not in allowed palette or explicitly disabled
const inSitePalette = activeTemplate?.allowedColorsSet ? activeTemplate.allowedColorsSet.has(key) : true;
const enabled = palette?.[key]?.enabled !== false;
if (!inSitePalette || !enabled) {
const inWplacePalette = activeTemplate?.allowedColorsSet ? activeTemplate.allowedColorsSet.has(key) : true;
// if (inWplacePalette) {
// key = 'other'; // Map all non-palette colors to "other"
// console.log('Added color to other');
// }
const isPaletteColorEnabled = palette?.[key]?.enabled !== false;
if (!inWplacePalette || !isPaletteColorEnabled) {
data[idx + 3] = 0; // hide disabled color center pixel
}
}
}
// Draws the template with somes colors disabled
filterCtx.putImageData(img, 0, 0);
context.drawImage(filterCanvas, Number(template.pixelCoords[0]) * this.drawMult, Number(template.pixelCoords[1]) * this.drawMult);
}
} catch (_) {
} catch (exception) {
// If filtering fails, we can log the error or handle it accordingly
console.warn('Failed to apply color filter:', exception);
// Fallback to drawing raw bitmap if filtering fails
context.drawImage(template.bitmap, Number(template.pixelCoords[0]) * this.drawMult, Number(template.pixelCoords[1]) * this.drawMult);
}
@ -457,6 +513,7 @@ export default class TemplateManager {
sum + (t.requiredPixelCount || t.pixelCount || 0), 0);
const totalRequired = totalRequiredTemplates > 0 ? totalRequiredTemplates : aggRequiredTiles;
// Turns numbers into formatted number strings. E.g., 1234 -> 1,234 OR 1.234 based on location of user
const paintedStr = new Intl.NumberFormat().format(aggPainted);
const requiredStr = new Intl.NumberFormat().format(totalRequired);
const wrongStr = new Intl.NumberFormat().format(totalRequired - aggPainted); // Used to be aggWrong, but that is bugged
@ -549,7 +606,7 @@ export default class TemplateManager {
if (a < 64) { continue; }
if (r === 222 && g === 250 && b === 206) { continue; }
requiredPixelCount++;
const key = `${r},${g},${b}`;
const key = activeTemplate.allowedColorsSet.has(`${r},${g},${b}`) ? `${r},${g},${b}` : 'other';
paletteMap.set(key, (paletteMap.get(key) || 0) + 1);
}
}