Partially working template

This commit is contained in:
SwingTheVine 2025-07-30 07:23:36 -04:00
parent 6face92136
commit fe62f14357
8 changed files with 104 additions and 58 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-294-black?style=flat"></a>
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-327-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-70.19%25-blue"></a>

4
package-lock.json generated
View file

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

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.63.20",
"version": "0.63.53",
"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.63.20
// @version 0.63.53
// @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

@ -28,7 +28,7 @@ export default class ApiManager {
spontaneousResponseListener(overlay) {
// Triggers whenever a message is sent
window.addEventListener('message', (event) => {
window.addEventListener('message', async (event) => {
const data = event.data; // The data of the message
const dataJSON = data['jsonData']; // The JSON response, if any
@ -110,12 +110,26 @@ export default class ApiManager {
const blobUUID = data['blobID'];
const blobData = data['blobData'];
const gojo = this.templateManager.drawGojo();
let templateBlob = blobData; // By default, apply no template
console.log(`templateState: ${this.templateManager.templateState || null}`);
// Only apply the template if a template is loaded
// Otherwise, draw the template so the next attempted load will not need a re-draw
switch (this.templateManager.templateState) {
case 'file': // Draw the template
console.log(`Attempting to draw template...`);
templateBlob = await this.templateManager.drawTemplate(blobData);
break;
case 'template': // The template is already processed, pass it in
templateBlob = this.templateManager.template;
break;
}
window.postMessage({
source: 'blue-marble',
blobID: blobUUID,
blobData: gojo,
blobData: templateBlob,
blink: data['blink']
});
break;

View file

@ -239,8 +239,6 @@ function buildOverlayMain() {
templateManager.setTemplateImage(input.files[0]);
templateManager.tempDraw();
console.log(templateManager.canvasTemplate);
instance.handleDisplayStatus(`Drew to canvas!`);
}
}).buildElement()

View file

@ -10,10 +10,6 @@ export default class TemplateManager {
this.canvasTemplate = null; // Our canvas
this.canvasTemplateID = 'bm-canvas'; // Our canvas ID
this.canvasMainID = 'div#map canvas.maplibregl-canvas'; // The selector for the main canvas
this.canvasMainMap = window.__bm_interceptedMap;
this.onMove = this.onMove.bind(this); // Binds the handler for `move` to this class instance's function
this.onResize = this.onResize.bind(this); // Binds the handler for `resize` to this class instance's function
this.onZoom = this.onZoom.bind(this); // Binds the handler for `zoom` to this class instance's function
this.template = null; // The template image.
this.templateState = ''; // The state of the template ('blob', 'proccessing', 'template', etc.)
}
@ -23,6 +19,7 @@ export default class TemplateManager {
* @param {string} selector - The CSS selector to use to find the canvas.
* @returns {HTMLCanvasElement|null} The canvas as an HTML Canvas Element, or null if the canvas does not exist
* @since 0.58.3
* @deprecated Not in use since 0.63.25
*/
getCanvas() {
@ -66,57 +63,94 @@ export default class TemplateManager {
this.template = file;
this.templateState = 'file';
const url = URL.createObjectURL(file); // 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
// const url = URL.createObjectURL(file); // 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
}
tempDraw() {
const ctx = this.getCanvas(this.canvasTemplateID)?.getContext('2d');
if (ctx) {
function drawLoop() {
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 100);
//ctx.setTransform(1, 0, 0, 1, -21511, -1305644);
//requestAnimationFrame(drawLoop);
}
drawLoop();
}
}
async drawTemplate(tileBlob) {
drawGojo() {
// Only continue if template state is NOT 'file' NOR 'template'
if (!((this.templateState == 'file') || (this.templateState == 'template'))) {return;}
const tileSize = 1000; // Pixels in a tile
const drawMult = 3; // Multiplier of draw size
const drawSize = tileSize * drawMult; // Draw multiplier
// const [templateBitmap, tileBitmap] = await Promise.all([
// createImageBitmap(await this.shrinkPixelsInPlace(this.template)),
// createImageBitmap(tileBlob)
// ]);
if (this.templateState != 'file') {return;}
// If the template has already been drawn, don't draw it again
console.log(this.template);
const templateBitmap = this.templateState == 'template' ? this.template : await createImageBitmap(await this.shreadBlob(this.template));
const tileBitmap = await createImageBitmap(tileBlob);
return this.template;
const canvas = new OffscreenCanvas(drawSize, drawSize);
const context = canvas.getContext('2d');
context.imageSmoothingEnabled = false; // Nearest neighbor
context.clearRect(0, 0, drawSize, drawSize); // Draws transparent background
context.drawImage(templateBitmap, 0, 0); // TODO: Change X Y here
context.drawImage(tileBitmap, 0, 0, drawSize, drawSize);
const final = await canvas.convertToBlob({ type: 'image/png' });
// If the template is not drawn yet...
if (this.templateState != 'template') {
// (99% chance templateState is 'file')
this.template = templateBitmap; // Store the drawn template
this.templateState = 'template'; // Indicate that the template has been drawn
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
}
return final;
}
/** What to do to our canvas when the canvas is panned.
* @since 0.60.10
/** Shreads the blob so that every pixel is surrounded by adjacent transparent pixels
* @param {Blob|File} blob - The blob to manipulate
* @param {number} [shrinkFactor=3] - An odd number that will place each pixel in the center. Even numbers will be increased by 1
* @param {string} [fileType='image/png'] - The File type to output as. PNG is one of the few transparent types.
* @returns {Promise<File>} - A Promise that resolved to a image/png file blob
* @since 0.63.37
*/
onMove() {
this.tempDraw();
console.log(this.canvasMainMap);
}
async shreadBlob(blob, shreadSize = 3, fileType = 'image/png') {
const bitmap = await createImageBitmap(blob); // Creates a bitmap image
/** What to do to our canvas when the canvas is zoomes.
* @since 0.60.11
*/
onZoom() {
this.tempDraw();
}
shreadSize |= 1; // Converts shreadSize to always be odd by forcing the right-most bit to be 1.
/** What to do to our canvas when the viewport is resized.
* @since 0.60.10
*/
onResize() {
const canvasMain = document.querySelector(this.canvasMainID);
const canvasTemplate = this.getCanvas();
if (!canvasTemplate || !canvasMain) {return;}
canvasTemplate.style.height = `${canvasMain?.clientHeight * (window.devicePixelRatio || 1)}px`;
canvasTemplate.style.width = `${canvasMain?.clientWidth * (window.devicePixelRatio || 1)}px`;
canvasTemplate.height = canvasMain?.clientHeight * (window.devicePixelRatio || 1);
canvasTemplate.width = canvasMain?.clientWidth * (window.devicePixelRatio || 1);
this.tempDraw();
const width = bitmap.width * Math.round(shreadSize); // Width of the canvas based on shread size times blob
const height = bitmap.height * Math.round(shreadSize); // Height of the canvas based on shread size times blob
const canvas = document.createElement('canvas'); // Creates a canvas
canvas.width = width;
canvas.height = height;
const context = canvas.getContext('2d'); // Gets the context of the canvas
context.imageSmoothingEnabled = false; // Nearest Neighbor scaling
context.drawImage(bitmap, 0, 0, width, height); // Fills the canvas with the blob
const imageData = context.getImageData(0, 0, width, height); // Data of the image on the canvas
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// For every pixel...
// ... Make it transparent unless it is the "center"
if ((x % shreadSize !== 1) || (y % shreadSize !== 1)) {
const pixelIndex = (y * width + 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
}
}
}
context.putImageData(imageData, 0, 0);
return new Promise((resolve) => {canvas.toBlob(resolve, fileType);});
}
}