Multi-Tile templates sorta work

This commit is contained in:
SwingTheVine 2025-08-01 20:57:49 -04:00
parent f84fbea0ce
commit 031f114433
8 changed files with 102 additions and 30 deletions

File diff suppressed because one or more lines are too long

View file

@ -35,7 +35,7 @@
<a href="https://github.com/SwingTheVine/Wplace-BlueMarble/blob/main/LICENSE.txt" target="_blank"><img alt="Software License: MPL-2.0" src="https://img.shields.io/badge/Software_License-MPL--2.0-brightgreen?style=flat"></a>
<a href="https://discord.gg/tpeBPy46hf" target="_blank"><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="" target="_blank"><img alt="WakaTime" src="https://img.shields.io/badge/Coding_Time-59hrs_0mins-blue?style=flat&logo=wakatime&logoColor=black&logoSize=auto&labelColor=white"></a>
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-405-black?style=flat"></a>
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-428-black?style=flat"></a>
<a href="" target="_blank"><img alt="Total Lines of Code" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=code"></a>
<a href="" target="_blank"><img alt="Total Comments" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=comments"></a>
<a href="" target="_blank"><img alt="Compression" src="https://img.shields.io/badge/Compression-73.03%25-blue"></a>

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "wplace-bluemarble",
"version": "0.65.51",
"version": "0.65.74",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wplace-bluemarble",
"version": "0.65.51",
"version": "0.65.74",
"devDependencies": {
"esbuild": "^0.25.0",
"terser": "^5.43.1"

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.65.51",
"version": "0.65.74",
"type": "module",
"scripts": {
"build": "node build/build.js",

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.65.51
// @version 0.65.74
// @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

@ -76,27 +76,31 @@ export default class Template {
console.log(`Draw Size X: ${drawSizeX}\nDraw Size Y: ${drawSizeY}`);
console.log(`Draw X: ${drawSizeX}\nDraw Y: ${drawSizeY}\nCanvas Width: ${drawSizeX * shreadSize}\nCanvas Height: ${drawSizeY * shreadSize}`);
// Change the canvas size and wipe the canvas
canvas.width = drawSizeX * shreadSize;
canvas.height = drawSizeY * shreadSize;
const canvasWidth = (drawSizeX * shreadSize) + (this.coords[2] * shreadSize);
const canvasHeight = (drawSizeY * shreadSize) + (this.coords[3] * shreadSize);
canvas.width = canvasWidth;
canvas.height = canvasHeight;
console.log(`Draw X: ${drawSizeX}\nDraw Y: ${drawSizeY}\nCanvas Width: ${canvasWidth}\nCanvas Height: ${canvasHeight}`);
context.imageSmoothingEnabled = false; // Nearest neighbor
console.log(`Getting X ${pixelX}-${pixelX + drawSizeX}\nGetting Y ${pixelY}-${pixelY + drawSizeY}`);
// Draws the template segment on this tile segment
context.clearRect(0, 0, drawSizeX * shreadSize, drawSizeY * shreadSize); // Clear any previous drawing (only runs when canvas size does not change)
context.drawImage(bitmap, pixelX, pixelY, drawSizeX, drawSizeY, 0, 0, drawSizeX * shreadSize, drawSizeY * shreadSize); // Coordinates and size of draw area of source image, then canvas
context.clearRect(0, 0, canvasWidth, canvasHeight); // Clear any previous drawing (only runs when canvas size does not change)
context.drawImage(bitmap, pixelX - this.coords[2], pixelY - this.coords[3], drawSizeX, drawSizeY, (pixelX % this.tileSize) * shreadSize, (pixelY % this.tileSize) * shreadSize, drawSizeX * shreadSize, drawSizeY * shreadSize); // Coordinates and size of draw area of source image, then canvas
const imageData = context.getImageData(0, 0, drawSizeX * shreadSize, drawSizeY * shreadSize); // Data of the image on the canvas
const imageData = context.getImageData(0, 0, canvasWidth, canvasHeight); // Data of the image on the canvas
for (let y = 0; y < drawSizeY * shreadSize; y++) {
for (let x = 0; x < drawSizeX * shreadSize; x++) {
for (let y = 0; y < canvasHeight; y++) {
for (let x = 0; x < canvasWidth; x++) {
// For every pixel...
// ... Make it transparent unless it is the "center"
if ((x % shreadSize !== 1) || (y % shreadSize !== 1)) {
const pixelIndex = (y * drawSizeX + x) * 4; // Find the pixel index in an array where every 4 indexes are 1 pixel
const pixelIndex = (y * canvasWidth + x) * 4; // Find the pixel index in an array where every 4 indexes are 1 pixel
imageData.data[pixelIndex + 3] = 0; // Make the pixel transparent on the alpha channel
}
}
@ -105,10 +109,16 @@ export default class Template {
console.log(`Shreaded pixels for ${pixelX}, ${pixelY}`, imageData);
context.putImageData(imageData, 0, 0);
templateTiles[`${(this.coords[0] + Math.floor(pixelX / 1000)).toString().padStart(4, '0')},${(this.coords[1] + Math.floor(pixelY / 1000)).toString().padStart(4, '0')},${(pixelX % 1000).toString().padStart(3, '0')},${(pixelY % 1000).toString().padStart(3, '0')}`] = await canvas.convertToBlob({ type: 'image/png' });
//templateTiles[`${(this.coords[0] + Math.floor(pixelX / 1000)).toString().padStart(4, '0')},${(this.coords[1] + Math.floor(pixelY / 1000)).toString().padStart(4, '0')},${(pixelX % 1000).toString().padStart(3, '0')},${(pixelY % 1000).toString().padStart(3, '0')}`] = await canvas.convertToBlob({ type: 'image/png' });
templateTiles[`${(this.coords[0] + Math.floor(pixelX / 1000)).toString().padStart(4, '0')},${(this.coords[1] + Math.floor(pixelY / 1000)).toString().padStart(4, '0')},${(pixelX % 1000).toString().padStart(3, '0')},${(pixelY % 1000).toString().padStart(3, '0')}`] = await createImageBitmap(canvas);
console.log(templateTiles);
// const final = await canvas.convertToBlob({ type: 'image/png' });
// const url = URL.createObjectURL(final); // Creates a blob URL
// window.open(url, '_blank'); // Opens a new tab with blob
// setTimeout(() => URL.revokeObjectURL(url), 10000); // Destroys the blob 10 seconds later
pixelX += drawSizeX;
}

View file

@ -123,17 +123,19 @@ export default class ApiManager {
const blobUUID = data['blobID'];
const blobData = data['blobData'];
let templateBlob = blobData; // By default, apply no template
// let templateBlob = blobData; // By default, apply no template
// Only run if all coordinates are there
if (this.templateCoordsTilePixel?.length >= 4) {
// if (this.templateCoordsTilePixel?.length >= 4) {
if ((tileCoordsTile[0] == this.templateCoordsTilePixel[0]) && (tileCoordsTile[1] == this.templateCoordsTilePixel[1])) {
// if ((tileCoordsTile[0] == this.templateCoordsTilePixel[0]) && (tileCoordsTile[1] == this.templateCoordsTilePixel[1])) {
console.log(`templateState: ${this.templateManager.templateState || null}`);
templateBlob = !!this.templateManager.templateState ? await this.templateManager.drawTemplate(blobData, this.templateCoordsTilePixel) : blobData;
}
}
// console.log(`templateState: ${this.templateManager.templateState || null}`);
// templateBlob = !!this.templateManager.templateState ? await this.templateManager.drawTemplate(blobData, this.templateCoordsTilePixel) : blobData;
// }
// }
const templateBlob = await this.templateManager.drawTemplateOnTile(blobData, tileCoordsTile);
window.postMessage({
source: 'blue-marble',

View file

@ -22,6 +22,7 @@ import { numberToEncoded } from "./utils";
* "1 $Z": {
* "name": "My Template",
* "URL": "https://github.com/SwingTheVine/Wplace-BlueMarble/blob/main/dist/assets/Favicon.png",
* "URLType": "template",
* "enabled": false,
* "tiles": {
* "375,1846,276,188": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA",
@ -44,6 +45,8 @@ export default class TemplateManager {
this.templatesVersion = '1.0.0'; // Version of JSON schema
this.userID = null; // The ID of the current user
this.encodingBase = '!#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~'; // Characters to use for encoding/decoding
this.tileSize = 1000; // The number of pixels in a tile. Assumes the tile is square
this.drawMult = 3; // The enlarged size for each pixel. E.g. when "3", a 1x1 pixel becomes a 1x1 pixel inside a 3x3 area. MUST BE ODD
// Template
this.canvasTemplate = null; // Our canvas
@ -119,8 +122,6 @@ export default class TemplateManager {
// Creates the JSON object if it does not already exist
if (!this.templatesJSON) {this.templatesJSON = await this.createJSON(); console.log(`Creating JSON...`);}
const tileSize = 1000; // The size of a tile in pixels
console.log(`Awaiting creation...`);
// Creates a new template instance
@ -131,12 +132,13 @@ export default class TemplateManager {
file: blob,
coords: coords
});
template.chunked = await template.createTemplateTiles(tileSize); // Chunks the tiles
template.chunked = await template.createTemplateTiles(this.tileSize); // Chunks the tiles
// Appends a child into the templates object
// The child's name is the number of templates already in the list (sort order) plus the encoded player ID
this.templatesJSON.templates[`${template.sortID} ${template.authorID}`] = {
"name": template.displayName, // Display name of template
"enabled": true,
"tiles": template.chunked
};
@ -161,9 +163,67 @@ export default class TemplateManager {
}
/** Draws all templates on that tile
* @param {File} tileBlob - The pixels that are placed on a tile
* @param {[number, number]} tileCoords - The tile coordinates [x, y]
*/
drawTemplateOnTile() {
async drawTemplateOnTile(tileBlob, tileCoords) {
const drawSize = this.tileSize * this.drawMult; // Draw multiplier
tileCoords = tileCoords[0].toString().padStart(4, '0') + ',' + tileCoords[1].toString().padStart(4, '0');
console.log(`Looking for "${tileCoords}"`);
const templateArray = this.templatesArray; // Stores a copy for sorting
// Sorts the array of Template class instances. 0 = first = lowest draw priority
templateArray.sort((a, b) => {
return a.sortID - b.sortID;
});
console.log(templateArray);
// Retrieves the relavent template tile blobs
const templateBlobs = templateArray
.map(template => {
const matchingTiles = Object.keys(template.chunked).filter(tile =>
tile.startsWith(tileCoords)
);
if (matchingTiles.length === 0) {return null;} // Return nothing when nothing is found
// Retrieves the blobs of the templates for this tile
const matchingTileBlobs = matchingTiles.map(tile => template.chunked[tile]);
return matchingTileBlobs?.[0];
})
.filter(Boolean);
console.log(templateBlobs);
const tileBitmap = await createImageBitmap(tileBlob);
const canvas = new OffscreenCanvas(drawSize, drawSize);
const context = canvas.getContext('2d');
context.imageSmoothingEnabled = false; // Nearest neighbor
// Tells the canvas to ignore anything outside of this area
context.beginPath();
context.rect(0, 0, drawSize, drawSize);
context.clip();
context.clearRect(0, 0, drawSize, drawSize); // Draws transparent background
context.drawImage(tileBitmap, 0, 0, drawSize, drawSize);
// For each template in this tile, draw them.
for (const templateBitmap of templateBlobs) {
console.log(`Template Blob is ${typeof templateBitmap}`);
console.log(templateBitmap);
context.drawImage(templateBitmap, tileCoords[0]*this.drawMult, tileCoords[1]*this.drawMult);
}
return await canvas.convertToBlob({ type: 'image/png' });
}
/** Imports the JSON object, and appends it to any JSON object already loaded