Merge branch 'main' of https://github.com/R74nCom/sandboxels
This commit is contained in:
commit
a0a634fe76
|
|
@ -0,0 +1,21 @@
|
||||||
|
(() => {
|
||||||
|
function runKeybind() {
|
||||||
|
promptInput("Input the keybind you want to run. (e.g. KeyA, Digit1, Backspace)", (keybind) => {
|
||||||
|
if (keybinds[keybind]) {
|
||||||
|
keybinds[keybind]();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
const keybindButton = document.createElement("button")
|
||||||
|
keybindButton.id = "keybindButton"
|
||||||
|
keybindButton.title = "Change static mode"
|
||||||
|
keybindButton.classList.add("controlButton")
|
||||||
|
keybindButton.onclick = () => {
|
||||||
|
runKeybind()
|
||||||
|
}
|
||||||
|
keybindButton.textContent = "Keybind"
|
||||||
|
document.getElementById("pauseButton").before(keybindButton)
|
||||||
|
}
|
||||||
|
})()
|
||||||
160
mods/static.js
160
mods/static.js
|
|
@ -8,14 +8,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomColor() {
|
function randomColor() {
|
||||||
const letters = "0123456789ABCDEF";
|
return '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, '0');
|
||||||
let color = "#";
|
|
||||||
for (let i = 0; i < 6; i++) {
|
|
||||||
color += letters[Math.floor(Math.random() * 16)];
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function loopScreen(callback) {
|
function loopScreen(callback) {
|
||||||
for (let x = 0; x <= width; x++) {
|
for (let x = 0; x <= width; x++) {
|
||||||
for (let y = 0; y <= height; y++) {
|
for (let y = 0; y <= height; y++) {
|
||||||
|
|
@ -24,19 +20,163 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keybinds["KeyS"] = () => {
|
/**
|
||||||
staticMode === 0 ? staticMode = 1 : staticMode === 1 ? staticMode = 2 : staticMode = 0
|
* Converts RGB to RGBA
|
||||||
|
* @param {string|object} rgb - Either "rgb(r, g, b)" string or {r, g, b} object
|
||||||
|
* @param {number} alpha - Alpha value between 0 and 1
|
||||||
|
* @returns {string} RGBA string
|
||||||
|
*/
|
||||||
|
function rgbToRgba(rgb, alpha = 1) {
|
||||||
|
let r, g, b;
|
||||||
|
|
||||||
|
if (typeof rgb === 'string') {
|
||||||
|
const match = rgb.match(/\d+/g);
|
||||||
|
if (!match || match.length < 3) throw new Error('Invalid RGB string');
|
||||||
|
[r, g, b] = match.map(Number);
|
||||||
|
} else if (typeof rgb === 'object') {
|
||||||
|
({ r, g, b } = rgb);
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid input: must be RGB string or object');
|
||||||
|
}
|
||||||
|
alpha = Math.min(Math.max(alpha, 0), 1);
|
||||||
|
|
||||||
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the alpha value of an rgba string
|
||||||
|
* @param {string} rgbaString - Example: "rgba(255, 0, 0, 0.5)"
|
||||||
|
* @param {number} newAlpha - New alpha value (0 to 1)
|
||||||
|
* @returns {string} - Updated rgba string
|
||||||
|
*/
|
||||||
|
function setAlpha(rgbaString, newAlpha) {
|
||||||
|
// Use a regex to capture the r, g, b values
|
||||||
|
const match = rgbaString.match(/rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\s*\)/);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error("Invalid rgba string: " + rgbaString);
|
||||||
|
}
|
||||||
|
const [_, r, g, b] = match;
|
||||||
|
return `rgba(${r}, ${g}, ${b}, ${newAlpha})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCircle(ctx, x, y, radius, options = {}) {
|
||||||
|
const { fill = 'blue', stroke = null, lineWidth = 1 } = options;
|
||||||
|
|
||||||
|
// Only compute offset if needed
|
||||||
|
const offset = lineWidth % 2 === 0 ? 0 : 0.5;
|
||||||
|
const cx = x + offset;
|
||||||
|
const cy = y + offset;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
|
||||||
|
|
||||||
|
// Set styles only if needed
|
||||||
|
if (fill) {
|
||||||
|
if (ctx.fillStyle !== fill) ctx.fillStyle = fill;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stroke) {
|
||||||
|
if (ctx.strokeStyle !== stroke) ctx.strokeStyle = stroke;
|
||||||
|
if (ctx.lineWidth !== lineWidth) ctx.lineWidth = lineWidth;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStaticMode() {
|
||||||
|
staticMode = (staticMode + 1) % 5
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
const staticButton = document.createElement("button")
|
||||||
|
staticButton.id = "staticButton"
|
||||||
|
staticButton.title = "Change static mode"
|
||||||
|
staticButton.classList.add("controlButton")
|
||||||
|
staticButton.onclick = () => {
|
||||||
|
toggleStaticMode()
|
||||||
|
}
|
||||||
|
staticButton.textContent = "Static"
|
||||||
|
document.getElementById("pauseButton").before(staticButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
keybinds["KeyS"] = () => {
|
||||||
|
toggleStaticMode()
|
||||||
|
}
|
||||||
|
keybinds["KeyK"] = () => {
|
||||||
|
toggleStaticMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Static rendering loop
|
||||||
|
let cachedColorMap = []
|
||||||
renderPostPixel(function (ctx) {
|
renderPostPixel(function (ctx) {
|
||||||
if (!staticMode) return
|
if (!staticMode) return
|
||||||
|
if (!paused) {
|
||||||
|
cachedColorMap = []
|
||||||
|
} else {
|
||||||
|
cachedColorMap.forEach(renderObj => {
|
||||||
|
let x = renderObj.x
|
||||||
|
let y = renderObj.y
|
||||||
|
let color = renderObj.color
|
||||||
|
if (color.match(/^#[0-9A-Fa-f]{6}$/)) {
|
||||||
|
color = rgbToRgba(hexToRGB(color), 1)
|
||||||
|
}
|
||||||
|
let colorFullAlpha = setAlpha(color, 1)
|
||||||
|
let isCircle = renderObj.circle
|
||||||
|
if (isCircle) {
|
||||||
|
drawCircle(ctx, canvasCoord(x) + 2.1, canvasCoord(y) + 2.1, 3, { fill: color })
|
||||||
|
} else {
|
||||||
|
drawSquare(ctx, colorFullAlpha, x, y, 1, 0.2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
loopScreen((x, y) => {
|
loopScreen((x, y) => {
|
||||||
if (staticMode === 1) {
|
if (staticMode === 1) {
|
||||||
drawSquare(ctx, randomGrayscale(), x, y, 1, 0.2)
|
let color = randomGrayscale()
|
||||||
|
drawSquare(ctx, color, x, y, 1, 0.2)
|
||||||
|
cachedColorMap.push({ x, y, color })
|
||||||
}
|
}
|
||||||
if (staticMode === 2) {
|
if (staticMode === 2) {
|
||||||
drawSquare(ctx, randomColor(), x, y, 1, 0.3)
|
let color = randomColor()
|
||||||
|
drawSquare(ctx, color, x, y, 1, 0.2)
|
||||||
|
cachedColorMap.push({ x, y, color })
|
||||||
}
|
}
|
||||||
|
if (staticMode === 3) {
|
||||||
|
let color = rgbToRgba(randomGrayscale(), 0.2)
|
||||||
|
drawCircle(ctx, canvasCoord(x) + 2.1, canvasCoord(y) + 2.1, 3, { fill: color })
|
||||||
|
cachedColorMap.push({ x, y, color, circle: true })
|
||||||
|
}
|
||||||
|
if (staticMode === 4) {
|
||||||
|
let color = rgbToRgba(hexToRGB(randomColor()), 0.2)
|
||||||
|
drawCircle(ctx, canvasCoord(x) + 2.1, canvasCoord(y) + 2.1, 3, { fill: color })
|
||||||
|
cachedColorMap.push({ x, y, color, circle: true })
|
||||||
|
}
|
||||||
|
// Currently broken with pausing
|
||||||
|
/*
|
||||||
|
if (staticMode === 5) {
|
||||||
|
let color = rgbToRgba(randomGrayscale(), 0.2)
|
||||||
|
const random = Math.random()
|
||||||
|
if (random <= 0.5) {
|
||||||
|
drawSquare(ctx, color, x, y)
|
||||||
|
cachedColorMap.push({ x, y, color })
|
||||||
|
} else {
|
||||||
|
drawCircle(ctx, canvasCoord(x) + 2.1, canvasCoord(y) + 2.1, 3, { fill: color })
|
||||||
|
cachedColorMap.push({ x, y, color, circle: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (staticMode === 6) {
|
||||||
|
let color = rgbToRgba(hexToRGB(randomColor()), 0.2)
|
||||||
|
const random = Math.random()
|
||||||
|
if (random <= 0.5) {
|
||||||
|
drawSquare(ctx, color, x, y)
|
||||||
|
cachedColorMap.push({ x, y, color })
|
||||||
|
} else {
|
||||||
|
drawCircle(ctx, canvasCoord(x) + 2.1, canvasCoord(y) + 2.1, 3, { fill: color })
|
||||||
|
cachedColorMap.push({ x, y, color, circle: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
|
|
|
||||||
140
mods/zoom.js
140
mods/zoom.js
|
|
@ -45,6 +45,140 @@
|
||||||
rescale()
|
rescale()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function gen_button(row, col, html, click, nopos, id){
|
||||||
|
const elem = document.createElement("button")
|
||||||
|
|
||||||
|
|
||||||
|
if (!nopos){
|
||||||
|
elem.style.gridColumn = row
|
||||||
|
elem.style.gridRow = col
|
||||||
|
}
|
||||||
|
if (id) { elem.id = id }
|
||||||
|
|
||||||
|
// Table for the data-pos to assign (row major). If null, don't add.
|
||||||
|
const data_pos_map = [
|
||||||
|
["tl", null, "tr"],
|
||||||
|
[null, null, null],
|
||||||
|
["bl", null, "br"]
|
||||||
|
]
|
||||||
|
|
||||||
|
elem.innerHTML = html
|
||||||
|
elem.onclick = click
|
||||||
|
|
||||||
|
if (data_pos_map[row-1][col-1] !== null) {
|
||||||
|
elem.dataset.pos = data_pos_map[row-1][col-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return elem
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_css(){
|
||||||
|
const FLOATER_CSS = `
|
||||||
|
#zm_data_div {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvasDiv {
|
||||||
|
overflow: hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
#zm_floater_container {
|
||||||
|
position: absolute;
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
right: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
height: 24%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
|
||||||
|
border: 2px solid white;
|
||||||
|
background-color: black;
|
||||||
|
font-size: 1.2em;
|
||||||
|
|
||||||
|
button { text-align: center; border: 0px solid white }
|
||||||
|
|
||||||
|
button:where([data-pos="tl"]) { border-width: 0px 2px 2px 0px };
|
||||||
|
button:where([data-pos="tr"]) { border-width: 2px 2px 0px 0px };
|
||||||
|
button:where([data-pos="bl"]) { border-width: 0px 0px 2px 2px };
|
||||||
|
button:where([data-pos="br"]) { border-width: 2px 0px 0px 2px };
|
||||||
|
}
|
||||||
|
#zm_floater_container:has(#zm_collapse[data-collapsed="true"]) {
|
||||||
|
height: calc(8% - 1px);
|
||||||
|
|
||||||
|
button:not(#zm_collapse) {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.zm_corner {
|
||||||
|
border: 2px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zm_collapse {
|
||||||
|
grid-row: 3;
|
||||||
|
grid-column: 3;
|
||||||
|
}
|
||||||
|
#zm_collapse[data-collapsed="true"] {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 1;
|
||||||
|
border-width: 0px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const style_div = document.createElement("style")
|
||||||
|
style_div.innerHTML = FLOATER_CSS
|
||||||
|
|
||||||
|
document.head.appendChild(style_div)
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_zoom_floaters(){
|
||||||
|
const container = document.createElement("div")
|
||||||
|
container.id = "zm_floater_container"
|
||||||
|
|
||||||
|
// Pan mode selector (C: Coarse F: Fine)
|
||||||
|
const pan_mode_sel = gen_button(
|
||||||
|
1,3, "C",
|
||||||
|
(evt) => {
|
||||||
|
evt.target.dataset.mode = evt.target.dataset.mode == "F" ? "C" : "F"
|
||||||
|
evt.target.innerText = evt.target.dataset.mode
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
"zm_panmode_sel"
|
||||||
|
)
|
||||||
|
|
||||||
|
const speed = () =>
|
||||||
|
(window.zoom_level > 3 ? 5 : 10) * // More granular at higher zoom levels
|
||||||
|
(pan_mode_sel.dataset.mode == "F" ? 0.25 : 1) // Increase granularity in fine mode
|
||||||
|
|
||||||
|
container.append(
|
||||||
|
// Direction buttons
|
||||||
|
gen_button(2,1, "↑", () => handle_pan("up" ,speed())),
|
||||||
|
gen_button(1,2, "←", () => handle_pan("left" ,speed())),
|
||||||
|
gen_button(3,2, "→", () => handle_pan("right" ,speed())),
|
||||||
|
gen_button(2,3, "↓", () => handle_pan("down" ,speed())),
|
||||||
|
|
||||||
|
// Zoom buttons
|
||||||
|
gen_button(1,1, "+", () => handle_zoom("in")),
|
||||||
|
gen_button(3,1, "-", () => handle_zoom("out")),
|
||||||
|
|
||||||
|
// Collapse button
|
||||||
|
gen_button(
|
||||||
|
3,3, "#",
|
||||||
|
(evt) => {
|
||||||
|
evt.target.dataset.collapsed = evt.target.dataset.collapsed == "true"
|
||||||
|
? "false"
|
||||||
|
: "true"
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
"zm_collapse"
|
||||||
|
),
|
||||||
|
pan_mode_sel
|
||||||
|
)
|
||||||
|
|
||||||
|
const canvas_div = document.getElementById("canvasDiv")
|
||||||
|
canvas_div.appendChild(container)
|
||||||
|
}
|
||||||
|
|
||||||
function rescale(){
|
function rescale(){
|
||||||
log_info()
|
log_info()
|
||||||
|
|
||||||
|
|
@ -87,8 +221,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function patch_ui(){
|
function patch_ui(){
|
||||||
|
add_css()
|
||||||
|
add_zoom_floaters()
|
||||||
|
|
||||||
zoom_data_div = document.createElement("div")
|
zoom_data_div = document.createElement("div")
|
||||||
document.getElementById("logDiv").appendChild(zoom_data_div)
|
zoom_data_div.id = "zm_data_div"
|
||||||
|
document.getElementById("logDiv").prepend(zoom_data_div)
|
||||||
|
|
||||||
const controls_table = document.getElementById("controlsTable").lastElementChild
|
const controls_table = document.getElementById("controlsTable").lastElementChild
|
||||||
controls_table.insertAdjacentHTML("beforeBegin",`
|
controls_table.insertAdjacentHTML("beforeBegin",`
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue