gimmickCellar

raywiper reupload

Jan 13th, 2026
78
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.90 KB | None | 0 0
  1. function getShapeRadiusFactor(shape, angle, nSides) {
  2. angle = angle % (Math.PI * 2);
  3. if (angle < 0) angle += Math.PI * 2;
  4. const starMath = (freq, innerRad) => 1 - (1 - innerRad) * Math.abs(Math.sin((freq * angle) / 2));
  5. switch (shape) {
  6. case "circle": return 1;
  7. case "square":
  8. let sqA = angle + (Math.PI / 4);
  9. return 1 / Math.max(Math.abs(Math.cos(sqA)), Math.abs(Math.sin(sqA)));
  10. case "diamond":
  11. return 1 / (Math.abs(Math.cos(angle)) + Math.abs(Math.sin(angle)));
  12. case "triangle": nSides = 3;
  13. case "ngon":
  14. if (!nSides || nSides < 3) nSides = 3;
  15. let thetaP = angle + Math.PI / 2;
  16. return Math.cos(Math.PI / nSides) / Math.cos((thetaP % (2 * Math.PI / nSides)) - (Math.PI / nSides));
  17. case "star":
  18. if (!nSides || nSides < 3) nSides = 5;
  19. return starMath(nSides, 0.4);
  20. case "burst": return 0.8 + Math.random() * 0.2;
  21. case "cross": return starMath(4, 0.25);
  22. case "flower":
  23. if (!nSides) nSides = 5;
  24. return 0.8 + 0.2 * Math.cos(nSides * angle);
  25. case "rose":
  26. return Math.abs(Math.cos((nSides || 4) * angle));
  27. case "astroid":
  28. let ast = Math.pow(Math.abs(Math.cos(angle)), 2/3) + Math.pow(Math.abs(Math.sin(angle)), 2/3);
  29. return 1 / Math.pow(ast, 1.5);
  30. case "squircle":
  31. let s4 = Math.pow(Math.cos(angle), 4) + Math.pow(Math.sin(angle), 4);
  32. return 1 / Math.pow(s4, 0.25);
  33. case "heart":
  34. let hA = angle - Math.PI / 2;
  35. let rH = 2 - 2 * Math.sin(hA) + Math.sin(hA) * (Math.sqrt(Math.abs(Math.cos(hA))) / (Math.sin(hA) + 1.4));
  36. return rH / 4;
  37. case "gear": return 1 + 0.1 * Math.sign(Math.sin((nSides || 8) * angle));
  38. case "clover": return Math.abs(Math.cos(2 * angle)) + 0.5;
  39. case "infinity": return Math.sqrt(Math.abs(Math.cos(2 * angle))) || 0.1;
  40. case "spiral_shape": return (angle / (Math.PI * 2));
  41. default: return 1;
  42. }
  43. }
  44.  
  45. async function runWiper(cfg) {
  46. const tc = (state && state.worldModel && state.worldModel.tileCols) || 16;
  47. const tr = (state && state.worldModel && state.worldModel.tileRows) || 8;
  48. const color = YourWorld.Color;
  49. const bgColor = YourWorld.BgColor;
  50. let cx, cy;
  51. if (window.cursorCoords) {
  52. cx = Math.floor(cursorCoords[0] * tc + cursorCoords[2]);
  53. cy = Math.floor(cursorCoords[1] * tr + cursorCoords[3]);
  54. } else {
  55. let center = w.getCenterCoords();
  56. cx = Math.floor(center[1] * tc);
  57. cy = Math.floor(center[0] * tr);
  58. }
  59. let pointMap = new Map();
  60. for (let i = 0; i < cfg.density; i++) {
  61. let theta = (i / cfg.density) * Math.PI * 2;
  62. let shapeFactor = getShapeRadiusFactor(cfg.shape, theta, cfg.sides);
  63. let tx = Math.round(cx + (Math.cos(theta) * cfg.radius * shapeFactor));
  64. let ty = Math.round(cy + (Math.sin(theta) * cfg.radius * shapeFactor) / 2);
  65. let path = lineGen(cx, cy, tx, ty);
  66. for (let p of path) {
  67. let px = Math.floor(p[0]);
  68. let py = Math.floor(p[1]);
  69. let key = `${px},${py}`;
  70. if (!pointMap.has(key)) {
  71. let dx = px - cx;
  72. let dy = (py - cy) * 2;
  73. let angle = Math.atan2(dy, dx);
  74. if (angle < 0) angle += Math.PI * 2;
  75. pointMap.set(key, { x: px, y: py, dist: dx*dx + dy*dy, angle: angle, dx: dx, dy: dy });
  76. }
  77. }
  78. }
  79. let points = Array.from(pointMap.values());
  80. const comparators = {
  81. "radius": (a, b) => a.dist - b.dist,
  82. "reverse_radius": (a, b) => b.dist - a.dist,
  83. "theta": (a, b) => a.angle - b.angle,
  84. "theta_rev": (a, b) => b.angle - a.angle,
  85. "reading": (a, b) => (a.y - b.y) || (a.x - b.x),
  86. "reading_rev": (a, b) => (b.y - a.y) || (b.x - a.x),
  87. "vertical": (a, b) => (a.x - b.x) || (a.y - b.y),
  88. "manhattan": (a, b) => (Math.abs(a.dx) + Math.abs(a.dy)) - (Math.abs(b.dx) + Math.abs(b.dy)),
  89. "chebyshev": (a, b) => Math.max(Math.abs(a.dx), Math.abs(a.dy)) - Math.max(Math.abs(b.dx), Math.abs(b.dy)),
  90. "random": () => Math.random() - 0.5,
  91. "checkerboard": (a, b) => (((a.x + a.y) % 2) & 1) - (((b.x + b.y) % 2) & 1) || (a.dist - b.dist),
  92. "spiral": (a, b) => (a.dist + (a.angle * (cfg.radius * 2))) - (b.dist + (b.angle * (cfg.radius * 2))),
  93. "waves": (a, b) => (Math.sin(a.x / 5) - Math.sin(b.x / 5)) || (a.y - b.y),
  94. "zigzag": (a, b) => (a.y - b.y) || (a.y % 2 === 0 ? a.x - b.x : b.x - a.x),
  95. "interlaced": (a, b) => (a.y % 4 - b.y % 4) || (a.y - b.y) || (a.x - b.x)
  96. };
  97. points.sort(comparators[cfg.order] || comparators["radius"]);
  98. const batchSize = cfg.fast ? 512 : 30;
  99. const now = Date.now();
  100. for (let i = 0; i < points.length; i += batchSize) {
  101. let chunk = points.slice(i, i + batchSize);
  102. if (cfg.fast) {
  103. let edits = [];
  104. chunk.forEach(p => {
  105. let tx = Math.floor(p.x / tc);
  106. let ty = Math.floor(p.y / tr);
  107. let inX = p.x - (tx * tc);
  108. let inY = p.y - (ty * tr);
  109. let tile = Tile.get(tx, ty);
  110. if (!tile) { Tile.set(tx, ty, blankTile()); tile = Tile.get(tx, ty); }
  111. tile.content[inY * tc + inX] = cfg.char;
  112. if (!tile.properties.color) tile.properties.color = new Array(tc * tr).fill(0);
  113. tile.properties.color[inY * tc + inX] = color;
  114. if (bgColor !== -1) {
  115. if (!tile.properties.bgcolor) tile.properties.bgcolor = new Array(tc * tr).fill(-1);
  116. tile.properties.bgcolor[inY * tc + inX] = bgColor;
  117. }
  118. let edit = [ty, tx, inY, inX, now, cfg.char, nextObjId++];
  119. if (color) edit.push(color);
  120. if (bgColor !== -1) { if (edit.length === 7) edit.push(0); edit.push(bgColor); }
  121. edits.push(edit);
  122. w.setTileRedraw(tx, ty, true);
  123. });
  124. w.net.write(edits);
  125. } else {
  126. chunk.forEach(p => writeCharToXY(cfg.char, color, p.x, p.y, bgColor));
  127. }
  128. if (cfg.wait > 0) await new Promise(r => setTimeout(r, cfg.wait));
  129. }
  130. }
  131.  
  132. function addSelect(modal, labelText, options, defaultVal) {
  133. let container = modal.inputField;
  134. let lab = document.createElement("label");
  135. lab.innerText = labelText + ":";
  136. lab.style.marginRight = "3px";
  137. let sel = document.createElement("select");
  138. sel.style.width = "150px";
  139. for (let key in options) {
  140. let opt = document.createElement("option");
  141. opt.value = key;
  142. opt.innerText = options[key];
  143. if (key === defaultVal) opt.selected = true;
  144. sel.appendChild(opt);
  145. }
  146. container.appendChild(lab);
  147. container.appendChild(sel);
  148. return { input: sel };
  149. }
  150.  
  151. var wiperMdl = new Modal();
  152. wiperMdl.createForm();
  153. wiperMdl.setFormTitle("Wiper Tool", {bold: true});
  154. var iRad = wiperMdl.addEntry("Radius", "text", "number");
  155. var iDen = wiperMdl.addEntry("Rays", "text", "number");
  156. var sShape = addSelect(wiperMdl, "Shape", {
  157. "circle": "Circle", "square": "Square", "diamond": "Diamond", "triangle": "Triangle", "ngon": "N-Gon",
  158. "star": "Star", "cross": "Cross", "astroid": "Astroid", "flower": "Flower", "rose": "Rose",
  159. "heart": "Heart", "squircle": "Squircle", "burst": "Burst", "gear": "Gear", "clover": "Clover", "infinity": "Infinity", "spiral_shape": "Spiral"
  160. }, "circle");
  161. var sOrder = addSelect(wiperMdl, "Order", {
  162. "radius": "Radius Out", "reverse_radius": "Radius In", "manhattan": "Manhattan", "chebyshev": "Chebyshev",
  163. "theta": "Theta CW", "theta_rev": "Theta CCW", "reading": "Reading LTR", "reading_rev": "Reading RTL",
  164. "vertical": "Vertical", "checkerboard": "Checkerboard", "spiral": "Spiral", "random": "Random", "waves": "Waves", "zigzag": "ZigZag", "interlaced": "Interlaced"
  165. }, "radius");
  166. var iSide = wiperMdl.addEntry("Sides", "text", "number");
  167. var iChar = wiperMdl.addEntry("Char", "text");
  168. var iWait = wiperMdl.addEntry("Wait", "text", "number");
  169. wiperMdl.setFooter();
  170. var fastMode = false;
  171. wiperMdl.setFooterCheckbox("Fast Mode", (v) => fastMode = v, false);
  172. wiperMdl.alignForm();
  173. iRad.input.value = "30"; iDen.input.value = "150"; iSide.input.value = "5"; iChar.input.value = " "; iWait.input.value = "10";
  174. wiperMdl.onSubmit(() => {
  175. runWiper({
  176. radius: parseInt(iRad.input.value),
  177. density: parseInt(iDen.input.value),
  178. shape: sShape.input.value,
  179. order: sOrder.input.value,
  180. sides: parseInt(iSide.input.value),
  181. char: iChar.input.value || " ",
  182. wait: parseInt(iWait.input.value),
  183. fast: fastMode
  184. });
  185. });
  186. if (w.menu) w.menu.addOption("Wiper Tool", () => wiperMdl.open());
Comments
Add Comment
Please, Sign In to add comment