Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- function getShapeRadiusFactor(shape, angle, nSides) {
- angle = angle % (Math.PI * 2);
- if (angle < 0) angle += Math.PI * 2;
- const starMath = (freq, innerRad) => 1 - (1 - innerRad) * Math.abs(Math.sin((freq * angle) / 2));
- switch (shape) {
- case "circle": return 1;
- case "square":
- let sqA = angle + (Math.PI / 4);
- return 1 / Math.max(Math.abs(Math.cos(sqA)), Math.abs(Math.sin(sqA)));
- case "diamond":
- return 1 / (Math.abs(Math.cos(angle)) + Math.abs(Math.sin(angle)));
- case "triangle": nSides = 3;
- case "ngon":
- if (!nSides || nSides < 3) nSides = 3;
- let thetaP = angle + Math.PI / 2;
- return Math.cos(Math.PI / nSides) / Math.cos((thetaP % (2 * Math.PI / nSides)) - (Math.PI / nSides));
- case "star":
- if (!nSides || nSides < 3) nSides = 5;
- return starMath(nSides, 0.4);
- case "burst": return 0.8 + Math.random() * 0.2;
- case "cross": return starMath(4, 0.25);
- case "flower":
- if (!nSides) nSides = 5;
- return 0.8 + 0.2 * Math.cos(nSides * angle);
- case "rose":
- return Math.abs(Math.cos((nSides || 4) * angle));
- case "astroid":
- let ast = Math.pow(Math.abs(Math.cos(angle)), 2/3) + Math.pow(Math.abs(Math.sin(angle)), 2/3);
- return 1 / Math.pow(ast, 1.5);
- case "squircle":
- let s4 = Math.pow(Math.cos(angle), 4) + Math.pow(Math.sin(angle), 4);
- return 1 / Math.pow(s4, 0.25);
- case "heart":
- let hA = angle - Math.PI / 2;
- let rH = 2 - 2 * Math.sin(hA) + Math.sin(hA) * (Math.sqrt(Math.abs(Math.cos(hA))) / (Math.sin(hA) + 1.4));
- return rH / 4;
- case "gear": return 1 + 0.1 * Math.sign(Math.sin((nSides || 8) * angle));
- case "clover": return Math.abs(Math.cos(2 * angle)) + 0.5;
- case "infinity": return Math.sqrt(Math.abs(Math.cos(2 * angle))) || 0.1;
- case "spiral_shape": return (angle / (Math.PI * 2));
- default: return 1;
- }
- }
- async function runWiper(cfg) {
- const tc = (state && state.worldModel && state.worldModel.tileCols) || 16;
- const tr = (state && state.worldModel && state.worldModel.tileRows) || 8;
- const color = YourWorld.Color;
- const bgColor = YourWorld.BgColor;
- let cx, cy;
- if (window.cursorCoords) {
- cx = Math.floor(cursorCoords[0] * tc + cursorCoords[2]);
- cy = Math.floor(cursorCoords[1] * tr + cursorCoords[3]);
- } else {
- let center = w.getCenterCoords();
- cx = Math.floor(center[1] * tc);
- cy = Math.floor(center[0] * tr);
- }
- let pointMap = new Map();
- for (let i = 0; i < cfg.density; i++) {
- let theta = (i / cfg.density) * Math.PI * 2;
- let shapeFactor = getShapeRadiusFactor(cfg.shape, theta, cfg.sides);
- let tx = Math.round(cx + (Math.cos(theta) * cfg.radius * shapeFactor));
- let ty = Math.round(cy + (Math.sin(theta) * cfg.radius * shapeFactor) / 2);
- let path = lineGen(cx, cy, tx, ty);
- for (let p of path) {
- let px = Math.floor(p[0]);
- let py = Math.floor(p[1]);
- let key = `${px},${py}`;
- if (!pointMap.has(key)) {
- let dx = px - cx;
- let dy = (py - cy) * 2;
- let angle = Math.atan2(dy, dx);
- if (angle < 0) angle += Math.PI * 2;
- pointMap.set(key, { x: px, y: py, dist: dx*dx + dy*dy, angle: angle, dx: dx, dy: dy });
- }
- }
- }
- let points = Array.from(pointMap.values());
- const comparators = {
- "radius": (a, b) => a.dist - b.dist,
- "reverse_radius": (a, b) => b.dist - a.dist,
- "theta": (a, b) => a.angle - b.angle,
- "theta_rev": (a, b) => b.angle - a.angle,
- "reading": (a, b) => (a.y - b.y) || (a.x - b.x),
- "reading_rev": (a, b) => (b.y - a.y) || (b.x - a.x),
- "vertical": (a, b) => (a.x - b.x) || (a.y - b.y),
- "manhattan": (a, b) => (Math.abs(a.dx) + Math.abs(a.dy)) - (Math.abs(b.dx) + Math.abs(b.dy)),
- "chebyshev": (a, b) => Math.max(Math.abs(a.dx), Math.abs(a.dy)) - Math.max(Math.abs(b.dx), Math.abs(b.dy)),
- "random": () => Math.random() - 0.5,
- "checkerboard": (a, b) => (((a.x + a.y) % 2) & 1) - (((b.x + b.y) % 2) & 1) || (a.dist - b.dist),
- "spiral": (a, b) => (a.dist + (a.angle * (cfg.radius * 2))) - (b.dist + (b.angle * (cfg.radius * 2))),
- "waves": (a, b) => (Math.sin(a.x / 5) - Math.sin(b.x / 5)) || (a.y - b.y),
- "zigzag": (a, b) => (a.y - b.y) || (a.y % 2 === 0 ? a.x - b.x : b.x - a.x),
- "interlaced": (a, b) => (a.y % 4 - b.y % 4) || (a.y - b.y) || (a.x - b.x)
- };
- points.sort(comparators[cfg.order] || comparators["radius"]);
- const batchSize = cfg.fast ? 512 : 30;
- const now = Date.now();
- for (let i = 0; i < points.length; i += batchSize) {
- let chunk = points.slice(i, i + batchSize);
- if (cfg.fast) {
- let edits = [];
- chunk.forEach(p => {
- let tx = Math.floor(p.x / tc);
- let ty = Math.floor(p.y / tr);
- let inX = p.x - (tx * tc);
- let inY = p.y - (ty * tr);
- let tile = Tile.get(tx, ty);
- if (!tile) { Tile.set(tx, ty, blankTile()); tile = Tile.get(tx, ty); }
- tile.content[inY * tc + inX] = cfg.char;
- if (!tile.properties.color) tile.properties.color = new Array(tc * tr).fill(0);
- tile.properties.color[inY * tc + inX] = color;
- if (bgColor !== -1) {
- if (!tile.properties.bgcolor) tile.properties.bgcolor = new Array(tc * tr).fill(-1);
- tile.properties.bgcolor[inY * tc + inX] = bgColor;
- }
- let edit = [ty, tx, inY, inX, now, cfg.char, nextObjId++];
- if (color) edit.push(color);
- if (bgColor !== -1) { if (edit.length === 7) edit.push(0); edit.push(bgColor); }
- edits.push(edit);
- w.setTileRedraw(tx, ty, true);
- });
- w.net.write(edits);
- } else {
- chunk.forEach(p => writeCharToXY(cfg.char, color, p.x, p.y, bgColor));
- }
- if (cfg.wait > 0) await new Promise(r => setTimeout(r, cfg.wait));
- }
- }
- function addSelect(modal, labelText, options, defaultVal) {
- let container = modal.inputField;
- let lab = document.createElement("label");
- lab.innerText = labelText + ":";
- lab.style.marginRight = "3px";
- let sel = document.createElement("select");
- sel.style.width = "150px";
- for (let key in options) {
- let opt = document.createElement("option");
- opt.value = key;
- opt.innerText = options[key];
- if (key === defaultVal) opt.selected = true;
- sel.appendChild(opt);
- }
- container.appendChild(lab);
- container.appendChild(sel);
- return { input: sel };
- }
- var wiperMdl = new Modal();
- wiperMdl.createForm();
- wiperMdl.setFormTitle("Wiper Tool", {bold: true});
- var iRad = wiperMdl.addEntry("Radius", "text", "number");
- var iDen = wiperMdl.addEntry("Rays", "text", "number");
- var sShape = addSelect(wiperMdl, "Shape", {
- "circle": "Circle", "square": "Square", "diamond": "Diamond", "triangle": "Triangle", "ngon": "N-Gon",
- "star": "Star", "cross": "Cross", "astroid": "Astroid", "flower": "Flower", "rose": "Rose",
- "heart": "Heart", "squircle": "Squircle", "burst": "Burst", "gear": "Gear", "clover": "Clover", "infinity": "Infinity", "spiral_shape": "Spiral"
- }, "circle");
- var sOrder = addSelect(wiperMdl, "Order", {
- "radius": "Radius Out", "reverse_radius": "Radius In", "manhattan": "Manhattan", "chebyshev": "Chebyshev",
- "theta": "Theta CW", "theta_rev": "Theta CCW", "reading": "Reading LTR", "reading_rev": "Reading RTL",
- "vertical": "Vertical", "checkerboard": "Checkerboard", "spiral": "Spiral", "random": "Random", "waves": "Waves", "zigzag": "ZigZag", "interlaced": "Interlaced"
- }, "radius");
- var iSide = wiperMdl.addEntry("Sides", "text", "number");
- var iChar = wiperMdl.addEntry("Char", "text");
- var iWait = wiperMdl.addEntry("Wait", "text", "number");
- wiperMdl.setFooter();
- var fastMode = false;
- wiperMdl.setFooterCheckbox("Fast Mode", (v) => fastMode = v, false);
- wiperMdl.alignForm();
- iRad.input.value = "30"; iDen.input.value = "150"; iSide.input.value = "5"; iChar.input.value = " "; iWait.input.value = "10";
- wiperMdl.onSubmit(() => {
- runWiper({
- radius: parseInt(iRad.input.value),
- density: parseInt(iDen.input.value),
- shape: sShape.input.value,
- order: sOrder.input.value,
- sides: parseInt(iSide.input.value),
- char: iChar.input.value || " ",
- wait: parseInt(iWait.input.value),
- fast: fastMode
- });
- });
- if (w.menu) w.menu.addOption("Wiper Tool", () => wiperMdl.open());