here2share

# tk_PostMessage_DOM_renderer.py

May 4th, 2026
126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.94 KB | None | 0 0
  1. # tk_PostMessage_DOM_renderer.py
  2.  
  3. # the only bottleneck is waiting on Python to LOAD the image onto its canvas. seems far better than numpy... etc.
  4.  
  5. import tkinter as tk
  6. from PIL import Image, ImageTk
  7. import subprocess
  8. import struct
  9.  
  10. JS_ENGINE = """
  11. const width = @w, height = @h;
  12. let zoom = 1.0, centerX = -0.5, centerY = 0, max_iter = 100;
  13. let mouseX = width / 2, mouseY = height / 2;
  14. let density = 7;
  15.  
  16. let dragging = false;
  17. let prevMouseX = 0;
  18. let prevMouseY = 0;
  19.  
  20. function hslToRgb(h, s, l) {
  21.    const hue2rgb = (p, q, t) => {
  22.        if (t < 0) t += 1;
  23.        if (t > 1) t -= 1;
  24.        if (t < 1/6) return p + (q - p) * 6 * t;
  25.        if (t < 1/2) return q;
  26.        if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
  27.        return p;
  28.    };
  29.    const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  30.    const p = 2 * l - q;
  31.    return [
  32.        Math.round(hue2rgb(p, q, h + 1/3) * 255),
  33.        Math.round(hue2rgb(p, q, h) * 255),
  34.        Math.round(hue2rgb(p, q, h - 1/3) * 255)
  35.    ];
  36. }
  37.  
  38. const colorCache = (() => {
  39.    const arr = new Uint8Array(max_iter * 3);
  40.    for (let i = 0; i < max_iter; i++) {
  41.        const [r, g, b] = hslToRgb(i / max_iter, 1, 0.5);
  42.        arr[i*3] = r;
  43.        arr[i*3+1] = g;
  44.        arr[i*3+2] = b;
  45.    }
  46.    return arr;
  47. })();
  48.  
  49. function panByPixels(dx, dy) {
  50.    centerX -= (dx / width) * 3.5 / zoom;
  51.    centerY -= (dy / height) * 2.0 / zoom;
  52. }
  53.  
  54. process.stdin.on('data', (data) => {
  55.    while (data.length >= 20) {
  56.        mouseX = data.readInt32LE(0);
  57.        mouseY = data.readInt32LE(4);
  58.        density = data.readInt32LE(8);
  59.        const mode = data.readInt32LE(12);
  60.        const reset = data.readInt32LE(16);
  61.  
  62.        if (reset === 1) {
  63.            zoom = 1.0;
  64.            centerX = -0.5;
  65.            centerY = 0;
  66.            dragging = false;
  67.            prevMouseX = mouseX;
  68.            prevMouseY = mouseY;
  69.            data = data.subarray(20);
  70.            continue;
  71.        }
  72.  
  73.        if (mode === 1) {
  74.            dragging = true;
  75.            prevMouseX = mouseX;
  76.            prevMouseY = mouseY;
  77.        } else if (mode === 2 && dragging) {
  78.            panByPixels(mouseX - prevMouseX, mouseY - prevMouseY);
  79.            prevMouseX = mouseX;
  80.            prevMouseY = mouseY;
  81.        } else if (mode === 3) {
  82.            dragging = false;
  83.        } else if (mode === 0 && !dragging) {
  84.            const mX = (mouseX / width * 3.5 - 2.5) / zoom + centerX;
  85.            const mY = (mouseY / height * 2.0 - 1.0) / zoom + centerY;
  86.  
  87.            zoom *= 1.06;
  88.            if (zoom > 1e13) {
  89.                zoom = 1.0;
  90.                centerX = -0.5;
  91.                centerY = 0;
  92.            }
  93.  
  94.            const newMX = (mouseX / width * 3.5 - 2.5) / zoom + centerX;
  95.            const newMY = (mouseY / height * 2.0 - 1.0) / zoom + centerY;
  96.  
  97.            centerX += (mX - newMX);
  98.            centerY += (mY - newMY);
  99.        }
  100.  
  101.        data = data.subarray(20);
  102.    }
  103. });
  104.  
  105. function render() {
  106.    const buffer = new Uint8ClampedArray(width * height * 3);
  107.    for (let i = 0; i < width * height; i++) {
  108.        let px = i % width;
  109.        let py = (i / width) | 0;
  110.        let x0 = (px / width * 3.5 - 2.5) / zoom + centerX;
  111.        let y0 = (py / height * 2.0 - 1.0) / zoom + centerY;
  112.        let x = 0, y = 0, iter = 0;
  113.        while (x*x + y*y <= 4 && iter < max_iter) {
  114.            let xt = x*x - y*y + x0;
  115.            y = 2*x*y + y0;
  116.            x = xt;
  117.            iter++;
  118.        }
  119.        let idx = i * 3;
  120.        if (iter === max_iter) {
  121.            buffer[idx] = buffer[idx+1] = buffer[idx+2] = 0;
  122.        } else {
  123.            let hueIndex = (iter * density) % max_iter;
  124.            buffer[idx] = colorCache[hueIndex*3];
  125.            buffer[idx+1] = colorCache[hueIndex*3+1];
  126.            buffer[idx+2] = colorCache[hueIndex*3+2];
  127.        }
  128.    }
  129.    process.stdout.write(Buffer.from(buffer));
  130.    setImmediate(render);
  131. }
  132.  
  133. render();
  134. """
  135.  
  136. w = 1200
  137. h = 600
  138. JS_ENGINE = JS_ENGINE.replace("@w", str(w)).replace("@h", str(h))
  139.  
  140. root = tk.Tk()
  141. root.title("# tk_PostMessage_DOM_renderer.py")
  142. root.geometry(f"{w}x{h+60}+-5+0")
  143. root.resizable(False, False)
  144.  
  145. canvas = tk.Canvas(root, width=w, height=h, bg="black", highlightthickness=0)
  146. canvas.pack()
  147.  
  148. slider = tk.Scale(root, from_=7, to=140, resolution=0.5, orient="horizontal", label="Freq~")
  149. slider.set(98)
  150. slider.pack(fill="x")
  151.  
  152. mx, my = w // 2, h // 2
  153. photo = None
  154. dragging = False
  155.  
  156. proc = subprocess.Popen(
  157.     ["node", "-e", JS_ENGINE],
  158.     stdin=subprocess.PIPE,
  159.     stdout=subprocess.PIPE,
  160.     bufsize=w * h * 3
  161. )
  162.  
  163. def send_packet(x, y, density, mode, reset=0):
  164.     try:
  165.         proc.stdin.write(struct.pack("<iiiii", int(x), int(y), int(density), int(mode), int(reset)))
  166.         proc.stdin.flush()
  167.     except:
  168.         pass
  169.  
  170. def on_press(event):
  171.     global mx, my, dragging
  172.     mx, my = event.x, event.y
  173.     dragging = True
  174.     send_packet(mx, my, slider.get(), 1, 0)
  175.  
  176. def on_release(event):
  177.     global mx, my, dragging
  178.     mx, my = event.x, event.y
  179.     dragging = False
  180.     send_packet(mx, my, slider.get(), 3, 0)
  181.  
  182. def on_drag(event):
  183.     global mx, my
  184.     mx, my = event.x, event.y
  185.     send_packet(mx, my, slider.get(), 2, 0)
  186.  
  187. def on_motion(event):
  188.     global mx, my
  189.     if not dragging:
  190.         mx, my = event.x, event.y
  191.         send_packet(mx, my, slider.get(), 0, 0)
  192.  
  193. def on_space(event):
  194.     send_packet(mx, my, slider.get(), 0, 1)
  195.  
  196. canvas.bind("<ButtonPress-1>", on_press)
  197. canvas.bind("<ButtonRelease-1>", on_release)
  198. canvas.bind("<B1-Motion>", on_drag)
  199. canvas.bind("<Motion>", on_motion)
  200. root.bind("<space>", on_space)
  201.  
  202. def update_loop():
  203.     global photo
  204.     send_packet(mx, my, slider.get(), 0, 0)
  205.     raw_bytes = proc.stdout.read(w * h * 3)
  206.     if raw_bytes:
  207.         img = Image.frombytes("RGB", (w, h), raw_bytes)
  208.         photo = ImageTk.PhotoImage(img)
  209.         canvas.delete("all")
  210.         canvas.create_image(0, 0, anchor="nw", image=photo)
  211.     root.after(1, update_loop)
  212.  
  213. update_loop()
  214. root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment