Template Overlay now appears above tile...

...and you can't move the template when placing pixels anymore xd
This commit is contained in:
SwingTheVine 2025-07-31 04:42:28 -04:00
parent c5c1ddd157
commit b0a98ee4a0
11 changed files with 118 additions and 17 deletions

File diff suppressed because one or more lines are too long

View file

@ -23,4 +23,5 @@ The favicon "Blue Marble" is owned by NASA
Special Thanks:
* nof, [darkness](https://github.com/TouchedByDarkness) for creating similar userscripts!
* [BullStein](https://github.com/BullStein), [allanf181](https://github.com/allanf181) for being early beta testers!
* TheBlueCorner for getting me interested in online pixel canvases!

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-342-black?style=flat"></a>
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-354-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-71.26%25-blue"></a>

4
package-lock.json generated
View file

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

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.64.0",
"version": "0.64.12",
"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.64.0
// @version 0.64.12
// @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

@ -3,17 +3,22 @@
* @since 0.0.2
* @example
* const overlay = new Overlay();
* overlay.addDiv('overlay')
* .addHeader(1, {'textContent': 'Your Overlay'}).buildElement()
* .addP({'textContent': 'This is your overlay. It is versatile.'}).buildElement()
* overlay.addDiv({ 'id': 'overlay' })
* .addDiv({ 'id': 'header' })
* .addHeader(1, {'textContent': 'Your Overlay'}).buildElement()
* .addP({'textContent': 'This is your overlay. It is versatile.'}).buildElement()
* .buildElement() // Marks the end of the header <div>
* .addHr().buildElement()
* .buildOverlay(document.body);
* // Output:
* // (Assume <body> already exists in the webpage)
* <body>
* <div id="overlay">
* <h1>Your Overlay</h1>
* <p>This is your overlay. It is versatile.</p>
* <div id="header">
* <h1>Your Overlay</h1>
* <p>This is your overlay. It is versatile.</p>
* </div>
* <hr>
* </div>
* </body>
*/

View file

@ -0,0 +1,86 @@
/** An instance of a template.
* Handles all mathmatics and manipulation regarding a single template.
* @since 0.65.2
*/
export default class Template {
/** The constructor for the {@link Template} class.
* @since 0.65.2
*/
constructor() {
this.templateName = ''; // The name of the template
this.templateFile = null; // The template file. This can be a pre-processed File, or a processed bitmap
this.templateState = ''; // The state of the template file. This is how you tell if it is a File, bitmap, or nothing
this.templateCoords = null; // The coordinates of the top left corner as (x, y, x, y)
this.templateChunked = null; // The affected chunks of the template, and their template for each chunk
}
/** Sets the template to the image passed in.
* @param {File} file - The file of the template image.
* @since 0.65.2
*/
setTemplateImage(file) {
this.templateName = file.name.replace(/\.[^/.]+$/, ''); // "foo.bar.png" -> "foo.bar"
this.template = file; // Overrides The previous template image/bitmap with the new image
this.templateState = 'file'; // Indicates that the template is now an image (not a bitmap)
// 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
}
/** Draws the template on the tile. Returns the tile plus the overlay.
* @param {File|Blob} tileBlob - The blob of the tile
* @param {Array<number, number, number, number>} [coordsTilePixel=[0,0,0,0]] - A number array of the four coordinates
* @returns {File|Blob} A image/png blob file
* @since 0.63.59
*/
async drawTemplate(tileBlob, coordsTilePixel=[0, 0, 0, 0]) {
// 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
coordsTilePixel = !!coordsTilePixel?.length ? coordsTilePixel : [0, 0, 0, 0]; // Set to default if [] passed in
console.log(this.template);
// If the template has already been drawn, don't draw it again
const templateBitmap = this.templateState == 'template' ? this.template : await createImageBitmap(await this.shreadBlob(this.template));
const tileBitmap = await createImageBitmap(tileBlob);
const canvas = new OffscreenCanvas(drawSize, drawSize);
const context = canvas.getContext('2d');
context.imageSmoothingEnabled = false; // Nearest neighbor scaleing
context.globalCompositeOperation = "destination-over"; // If we the image we are drawing has transparent pixels, don't preserve them.
// 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); // Draws the tile
context.drawImage(templateBitmap, coordsTilePixel[2]*3, coordsTilePixel[3]*3); // Draws the template on top of the tile
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, and this.template now stores a bitmap
// 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;
}
}

View file

@ -16,6 +16,7 @@ export default class ApiManager {
this.templateManager = templateManager;
this.disableAll = false; // Should the entire userscript be disabled?
this.coordsTilePixel = []; // Contains the last detected tile/pixel coordinate pair requested
this.templateCoordsTilePixel = []; // Contains the last "enabled" template coords
}
/** Determines if the spontaneously recieved response is something we want.
@ -107,7 +108,7 @@ export default class ApiManager {
case 'tiles':
// Runs only if the tile has the a template
// Runs only if the tile has the template
let tileCoordsTile = data['endpoint'].split('/');
tileCoordsTile = [parseInt(tileCoordsTile[tileCoordsTile.length - 2]), parseInt(tileCoordsTile[tileCoordsTile.length - 1].replace('.png', ''))];
@ -115,10 +116,14 @@ export default class ApiManager {
const blobData = data['blobData'];
let templateBlob = blobData; // By default, apply no template
if ((tileCoordsTile[0] == this.coordsTilePixel[0]) && (tileCoordsTile[1] == this.coordsTilePixel[1])) {
// Only run if all coordinates are there
if (this.templateCoordsTilePixel?.length >= 4) {
console.log(`templateState: ${this.templateManager.templateState || null}`);
templateBlob = !!this.templateManager.templateState ? await this.templateManager.drawTemplate(blobData, this.coordsTilePixel) : blobData;
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;
}
}
window.postMessage({

View file

@ -237,6 +237,9 @@ function buildOverlayMain() {
// Kills itself if there is no file
if (!input?.files[0]) {instance.handleDisplayError(`No file selected!`); return;}
console.log(`TCoords: ${apiManager.templateCoordsTilePixel}\nCoords: ${apiManager.coordsTilePixel}`);
apiManager.templateCoordsTilePixel = apiManager.coordsTilePixel; // Update template coords
console.log(`TCoords: ${apiManager.templateCoordsTilePixel}\nCoords: ${apiManager.coordsTilePixel}`);
templateManager.setTemplateImage(input.files[0]);
instance.handleDisplayStatus(`Drew to canvas!`);

View file

@ -102,8 +102,9 @@ export default class TemplateManager {
context.clip();
context.clearRect(0, 0, drawSize, drawSize); // Draws transparent background
context.drawImage(templateBitmap, coordsTilePixel[2]*3, coordsTilePixel[3]*3);
context.drawImage(tileBitmap, 0, 0, drawSize, drawSize);
//context.globalCompositeOperation = "destination-atop"; // If we the image we are drawing has transparent pixels, don't preserve them.
context.drawImage(templateBitmap, coordsTilePixel[2]*3, coordsTilePixel[3]*3);
const final = await canvas.convertToBlob({ type: 'image/png' });