commit 9cd120ed14199a40b41dc3e9fdcddd5589204ae6
Author: kingofdog <06D#vA75!qwD4AAJ>
Date: Sun Apr 26 15:40:03 2020 +0200
Mandelbrot init
diff --git a/mandelbrot/.vscode/launch.json b/mandelbrot/.vscode/launch.json
new file mode 100644
index 0000000..25368f3
--- /dev/null
+++ b/mandelbrot/.vscode/launch.json
@@ -0,0 +1,10 @@
+{
+ "version": "0.2.0",
+ "configurations": [{
+ "name": "Launch index.html",
+ "type": "firefox",
+ "request": "launch",
+ "reAttach": true,
+ "file": "${workspaceFolder}/index.html"
+ }]
+}
\ No newline at end of file
diff --git a/mandelbrot/index.html b/mandelbrot/index.html
new file mode 100644
index 0000000..7805d31
--- /dev/null
+++ b/mandelbrot/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Mandelbrot
+
+
+
+ Test
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mandelbrot/main.js b/mandelbrot/main.js
new file mode 100644
index 0000000..c85a151
--- /dev/null
+++ b/mandelbrot/main.js
@@ -0,0 +1,126 @@
+const canvas = document.getElementById("canvas");
+const ctx = canvas.getContext("2d");
+
+const width = 1000;
+const height = 1000;
+
+canvas.style.width = width + "px";
+canvas.style.height = height + "px";
+canvas.width = width;
+canvas.height = height;
+
+let minX = -2,
+ minY = -2,
+ maxX = 2,
+ maxY = 2;
+
+let selection = null;
+let isMouseDown = false;
+let mouseStartPosition = null;
+
+let mandelbrot = ctx.createImageData(width, height);
+
+function calcRe(col) {
+ return (col * (maxX - minX)) / width + minX;
+}
+
+function calcIm(row) {
+ return (row * (maxY - minY)) / height + minY;
+}
+
+function drawMandelbrotSet() {
+ ctx.putImageData(mandelbrot, 0, 0);
+}
+
+let needToDraw = false;
+
+function draw() {
+ if (needToDraw) {
+ ctx.clearRect(0, 0, width, height);
+ drawMandelbrotSet();
+ }
+
+ if (selection) {
+ ctx.fillStyle = "rgba(0, 0, 255, 0.8)";
+ ctx.fillRect(
+ selection.startX,
+ selection.startY,
+ selection.endX - selection.startX,
+ selection.endY - selection.startY
+ );
+
+ needToDraw = true;
+ }
+
+ setTimeout(draw, 1000 / 60);
+}
+
+canvas.addEventListener("mousedown", (event) => {
+ isMouseDown = true;
+ selection = {
+ startX: event.clientX - event.target.offsetLeft,
+ startY: event.clientY - event.target.offsetTop,
+ endX: event.clientX - event.target.offsetLeft,
+ endY: event.clientY - event.target.offsetTop,
+ };
+});
+
+canvas.addEventListener("mousemove", (event) => {
+ if (isMouseDown) {
+ const posX = event.clientX - event.target.offsetLeft;
+ const posY = event.clientY - event.target.offsetTop;
+ const dx = posX - selection.startX;
+ const dy = posY - selection.startY;
+ if (Math.abs(dx) > Math.abs(dy)) {
+ selection.endX = selection.startX + dx;
+ selection.endY = selection.startY + dx;
+ } else {
+ selection.endX = selection.startX + dy;
+ selection.endY = selection.startY + dy;
+ }
+ }
+});
+
+canvas.addEventListener("mouseup", (event) => {
+ isMouseDown = false;
+
+ const newMinX = calcRe(selection.startX);
+ const newMinY = calcIm(selection.startY);
+ const newMaxX = calcRe(selection.endX);
+ const newMaxY = calcIm(selection.endY);
+
+ minX = newMinX;
+ minY = newMinY;
+ maxX = newMaxX;
+ maxY = newMaxY;
+ updateWorker();
+
+ selection = null;
+});
+
+let worker;
+
+function updateWorker() {
+ worker.postMessage([{
+ mandelbrot: mandelbrot,
+ width,
+ height,
+ minX,
+ maxX,
+ minY,
+ maxY,
+ }]);
+}
+
+function initWorker() {
+ worker = new Worker('worker.js');
+ updateWorker();
+
+ worker.onmessage = (e) => {
+ mandelbrot = e.data[0];
+ needToDraw = true;
+ };
+}
+
+initWorker();
+draw();
\ No newline at end of file
diff --git a/mandelbrot/server/main.go b/mandelbrot/server/main.go
new file mode 100644
index 0000000..fbc8c18
--- /dev/null
+++ b/mandelbrot/server/main.go
@@ -0,0 +1,102 @@
+package main
+
+import (
+ "encoding/json"
+ "log"
+ "net/http"
+ "net/url"
+ "strconv"
+
+ "github.com/lucasb-eyer/go-colorful"
+)
+
+type Mandelbrot []uint
+
+func (m Mandelbrot) SetPixel(width int, x int, y int, color []uint) {
+ start := y*int(width)*4 + x*4
+ for i := 0; i < 4; i++ {
+ m[start+i] = color[i]
+ }
+}
+
+func GetColor(iteration uint, maxIterations uint) []uint {
+ c := colorful.Hsl(float64(iteration)/256, 1.0, float64(iteration)/(float64(iteration)+8.0))
+ return []uint{
+ uint(c.R * 255),
+ uint(c.G * 255),
+ uint(c.B * 255),
+ 255,
+ }
+ //return []uint{0, 0, 0, 255}
+}
+
+func CalculateMandelbrot(maxIterations uint, minX float64, minY float64, maxX float64, maxY float64, width int, height int) Mandelbrot {
+ mandelbrot := make(Mandelbrot, width*height*4)
+
+ dx := maxX - minX
+ dy := maxY - minY
+ for row := 0; row < height; row++ {
+ for col := 0; col < width; col++ {
+ cRe := (float64(col)*dx)/float64(width) + minX
+ cIm := (float64(row)*dy)/float64(height) + minY
+
+ var x float64
+ var y float64
+
+ var iteration uint
+ for ; x*x+y*y <= 4 && iteration < maxIterations; iteration++ {
+ newX := x*x - y*y + cRe
+ y = 2*x*y + cIm
+ x = newX
+ }
+
+ mandelbrot.SetPixel(width, col, row, GetColor(iteration, maxIterations))
+ }
+ }
+
+ return mandelbrot
+}
+
+func GetFloatParameter(query url.Values, name string, defaultValue float64) float64 {
+ if str := query.Get(name); len(str) > 0 {
+ value, err := strconv.ParseFloat(str, 64)
+ if err == nil {
+ return value
+ }
+ }
+ return defaultValue
+}
+
+func GetIntParameter(query url.Values, name string, defaultValue int) int {
+ if str := query.Get(name); len(str) > 0 {
+ value, err := strconv.ParseInt(str, 10, 64)
+ if err == nil {
+ return int(value)
+ }
+ }
+ return defaultValue
+}
+
+func MainHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+
+ query := r.URL.Query()
+
+ var maxIterations uint = 1000
+ minX := GetFloatParameter(query, "minX", -2.0)
+ minY := GetFloatParameter(query, "minY", -2.0)
+ maxX := GetFloatParameter(query, "maxX", 2.0)
+ maxY := GetFloatParameter(query, "maxY", 2.0)
+ width := GetIntParameter(query, "width", 1000)
+ height := GetIntParameter(query, "height", 1000)
+
+ log.Println(minX, minY, maxX, maxY, width, height)
+
+ json.NewEncoder(w).Encode(CalculateMandelbrot(maxIterations, minX, minY, maxX, maxY, width, height))
+}
+
+func main() {
+ http.HandleFunc("/", MainHandler)
+
+ log.Fatal(http.ListenAndServe(":8001", nil))
+}
diff --git a/mandelbrot/worker.js b/mandelbrot/worker.js
new file mode 100644
index 0000000..d8641e8
--- /dev/null
+++ b/mandelbrot/worker.js
@@ -0,0 +1,161 @@
+function calcRe(col) {
+ return (col * (maxX - minX)) / width + minX;
+}
+
+function calcIm(row) {
+ return (row * (maxY - minY)) / height + minY;
+}
+
+function hslToRgb(h, s, l) {
+ var r, g, b;
+
+ if (s == 0) {
+ r = g = b = l; // achromatic
+ } else {
+ var hue2rgb = function hue2rgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ };
+
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1 / 3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1 / 3);
+ }
+
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
+}
+
+function getColor(iterations, maxIterations) {
+ const rgb = hslToRgb(iterations / 256, 1, iterations / (iterations + 8));
+ return [...rgb, 255];
+}
+
+function setPixel(set, width, x, y, color) {
+ const startIndex = y * width * 4 + x * 4;
+ set[startIndex + 0] = color[0];
+ set[startIndex + 1] = color[1];
+ set[startIndex + 2] = color[2];
+ set[startIndex + 3] = color[3];
+ return set;
+}
+
+async function getMandelbrotSet(maxIter, minX, minY, maxX, maxY, width, height) {
+ let partSet = [];
+ for (let i = 0; i < width * height * 4; i++) {
+ partSet.push(0);
+ }
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const c_re = (col * (maxX - minX)) / width + minX;
+ const c_im = (row * (maxY - minY)) / height + minY;
+
+ let x = 0,
+ y = 0;
+
+ let iteration = 0;
+ for (; x ** 2 + y ** 2 <= 4 && iteration < maxIter; iteration++) {
+ const x_new = x * x - y * y + c_re;
+ y = 2 * x * y + c_im;
+ x = x_new;
+ }
+
+ partSet = setPixel(partSet, width, col, row, getColor(iteration, maxIter));
+ }
+ }
+
+ return partSet;
+}
+
+async function loadMandelbrotSet(resolution, minX, minY, maxX, maxY, width, height) {
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', `http://127.0.0.1:8001?minX=${minX}&maxX=${maxX}&minY=${minY}&maxY=${maxY}&width=${width}&height=${height}`, true)
+ xhr.send();
+
+ xhr.addEventListener('readystatechange', (e) => {
+ if (xhr.readyState == 4) {
+ const response = JSON.parse(xhr.responseText);
+ console.log(response);
+ resolve(response);
+ }
+ }, false);
+ });
+}
+
+async function getForResolution(resolution = 1000) {
+ const chunks = 4;
+ const chunkWidth = Math.round(width / chunks);
+ const chunkHeight = Math.round(height / chunks);
+
+ const dx = (maxX - minX) / chunks;
+ const dy = (maxY - minY) / chunks;
+
+ const promises = [];
+
+ for (let i = 0; i < chunks; i++) {
+ for (let j = 0; j < chunks; j++) {
+ promises.push(new Promise((resolve, reject) => {
+ const x = i;
+ const y = j;
+ loadMandelbrotSet(resolution,
+ minX + x * dx,
+ minY + y * dy,
+ minX + (x + 1) * dx,
+ minY + (y + 1) * dy,
+ chunkWidth,
+ chunkHeight
+ ).then(partSet => {
+ console.log('finished', x, y);
+
+ const startRow = y * chunkHeight;
+ const startCol = x * chunkWidth;
+ console.log(startRow, startCol, chunkHeight, chunkWidth);
+
+ for (let row = 0; row < chunkHeight; row++) {
+ for (let col = 0; col < chunkWidth; col++) {
+ for (let i = 0; i < 4; i++) {
+ mandelbrot.data[(row + startRow) * width * 4 + (startCol + col) * 4 + i] = partSet[row * chunkWidth * 4 + col * 4 + i];
+ }
+ }
+ }
+ sendMandelbrot();
+ resolve();
+ });
+ }));
+ }
+ }
+
+ await Promise.all(promises);
+}
+
+let mandelbrot;
+let width,
+ height;
+let minX,
+ maxX,
+ minY,
+ maxY;
+
+onmessage = function (e) {
+ const data = e.data[0];
+ mandelbrot = data.mandelbrot;
+ width = data.width;
+ height = data.height;
+ minX = data.minX;
+ minY = data.minY;
+ maxX = data.maxX;
+ maxY = data.maxY;
+
+ getForResolution();
+};
+
+function sendMandelbrot() {
+ postMessage([mandelbrot]);
+}
\ No newline at end of file