From 642b0b74b5d9f79280cc1d2663d6e172e7d2a802 Mon Sep 17 00:00:00 2001
From: koniifer <koniifer@proton.me>
Date: Sun, 15 Dec 2024 22:05:06 +0000
Subject: [PATCH] cooler mandelbrot

---
 Cargo.lock                                    |  14 +-
 .../render_example/src/examples/mandelbrot.hb | 238 +++++++++---------
 sysdata/system_config.toml                    |   8 +-
 3 files changed, 135 insertions(+), 125 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index d1819069..5ee0f130 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -88,18 +88,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "crossbeam-queue"
-version = "0.3.11"
+version = "0.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
 dependencies = [
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.20"
+version = "0.8.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
 
 [[package]]
 name = "derive_more"
@@ -213,12 +213,12 @@ dependencies = [
 [[package]]
 name = "hbbytecode"
 version = "0.1.0"
-source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#dc96c8b10ae85af7a11bd1e8ca2cd29e2c42a033"
+source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#9f43e3bb925534f613cb2089df6e5fe315fc851a"
 
 [[package]]
 name = "hblang"
 version = "0.1.0"
-source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#dc96c8b10ae85af7a11bd1e8ca2cd29e2c42a033"
+source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#9f43e3bb925534f613cb2089df6e5fe315fc851a"
 dependencies = [
  "hashbrown",
  "hbbytecode",
@@ -229,7 +229,7 @@ dependencies = [
 [[package]]
 name = "hbvm"
 version = "0.1.0"
-source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#dc96c8b10ae85af7a11bd1e8ca2cd29e2c42a033"
+source = "git+https://git.ablecorp.us/AbleOS/holey-bytes.git#9f43e3bb925534f613cb2089df6e5fe315fc851a"
 dependencies = [
  "hbbytecode",
 ]
diff --git a/sysdata/programs/render_example/src/examples/mandelbrot.hb b/sysdata/programs/render_example/src/examples/mandelbrot.hb
index 391bcc1c..15c55481 100644
--- a/sysdata/programs/render_example/src/examples/mandelbrot.hb
+++ b/sysdata/programs/render_example/src/examples/mandelbrot.hb
@@ -1,45 +1,66 @@
-// ln_f32 := fn(x: f32): f32 {
-// 	if x <= 0 {
-// 		return -1000000000000000.0
-// 	}
-// 	approx := x - 1.0
-// 	i := 0
-// 	loop if i == 10 break else {
-// 		exp_approx := exp_f32(approx)
-// 		approx -= (exp_approx - x) / exp_approx
-// 		i += 1
-// 	}
-// 	return approx
-// }
+ln_f64 := fn(x: f64): f64 {
+	if x <= 0.0 return -100000000000000000000000.0
+	if x == 1.0 return 0.0
+	if x == E return 1.0
 
-// exp_f32 := fn(x: f32): f32 {
-// 	sum := 1.0
-// 	term := 1.0
-// 	n := @as(int, 1)
-// 	loop if n == 20 break else {
-// 		term *= x / @floatcast(@itf(n))
-// 		sum += term
-// 		if term < 0.000006 break
-// 		n += 1
-// 	}
-// 	return sum
-// }
+	scale := @as(f64, 0.0)
+	scaled_x := x
 
-// sqrt_f32 := fn(x: f32): f32 {
-// 	if x <= 0 {
-// 		return 0.0
-// 	}
-// 	guess := x / 2.0
-// 	tolerance := 0.000001
-// 	loop {
-// 		new_guess := (guess + x / guess) / 2.0
-// 		if guess - new_guess < tolerance {
-// 			break
-// 		}
-// 		guess = new_guess
-// 	}
-// 	return guess
-// }
+	if scaled_x > 2.0 {
+		loop if scaled_x <= 2.0 break else {
+			scaled_x = scaled_x / E
+			scale += 1.0
+		}
+	} else if scaled_x < 1.0 {
+		loop if scaled_x >= 1.0 {
+			scaled_x = scaled_x * E
+			scale -= 1.0
+		}
+	}
+
+	guess := (scaled_x - 1.0) / (scaled_x + 1.0)
+
+	max_iter := 30
+	i := 0
+	loop if i == max_iter break else {
+		exp_g := exp_f64(guess)
+		f := exp_g - scaled_x
+		f_prime := exp_g
+
+		delta := f / (f_prime * (1.0 - 0.5 * f * f_prime / (f_prime * f_prime)))
+
+		guess = guess - delta
+
+		if abs_f64(delta) < 0.0000000001 break
+		i += 1
+	}
+
+	return guess + scale
+}
+
+exp_f64 := fn(x: f64): f64 {
+	result := @as(f64, 1.0)
+	term := @as(f64, 1.0)
+	n := @as(int, 1)
+
+	loop if n == 20 break else {
+		term = term * x / @itf(n)
+		result += term
+
+		if abs_f64(term) < 0.0000000001 break
+		n += 1
+	}
+
+	return result
+}
+
+abs_f64 := fn(x: f64): f64 {
+	if x < 0 return -x else return x
+}
+
+lerp_f64 := fn(v0: f64, v1: f64, t: f64): f64 {
+	return v0 + t * (v1 - v0)
+}
 
 render := @use("lib:render")
 sunset := @use("lib:sunset_proto");
@@ -57,22 +78,21 @@ $X_MAX := -0.93
 $Y_MIN := 0.31
 $Y_MAX := 0.306
 
-// if you use the minibrot this should be at least 100 to actually see the minibrot,
-// if you use the mandelbrot it looks best under 30
-$MAX_MAX_ITERATION := 30000000
+$MAX_ITERATION := 300
 
-$USE_SUNSET := true
+$USE_SUNSET := false
 
 $COLOUR_R := 200
 $COLOUR_G := 100
 $COLOUR_B := 255
 
-// $COLOUR_R := 255
-// $COLOUR_G := 255
-// $COLOUR_B := 255
+$LOG_2 := 0.693147180559945309417232121458176568
+$E := 2.71828182845904523536028747135266250
 
-// $INTERIOR_COLOUR := render.WHITE
-$INTERIOR_COLOUR := render.Color.{r: COLOUR_R, g: COLOUR_G, b: COLOUR_B, a: 255}
+$INTERIOR_COLOUR := render.BLACK
+
+palette := [render.Color].(render.RED, render.YELLOW, render.GREEN, render.CYAN, render.BLUE, render.MAGENTA)
+$LEN_PALETTE := @sizeof(@TypeOf(palette)) / @sizeof(render.Color)
 
 example := fn(): void {
 	screen := @as(render.Surface, idk)
@@ -90,74 +110,64 @@ example := fn(): void {
 	}
 	screen.clear(INTERIOR_COLOUR)
 
-	max_iteration := 0
-	prev_max_iteration := 0
+	x_scale := @as(f64, X_MAX - X_MIN) / @itf(@bitcast(screen.width))
+	y_scale := @as(f64, Y_MAX - Y_MIN) / @itf(@bitcast(screen.height))
 
-	x_scale := (X_MAX - X_MIN) / @floatcast(@itf(@bitcast(screen.width)))
-	y_scale := (Y_MAX - Y_MIN) / @floatcast(@itf(@bitcast(screen.height)))
+	py := 0
+	loop if py == screen.height break else {
+		px := 0
+		loop if px >= screen.width break else {
+			x0 := @as(f64, X_MIN) + @itf(@bitcast(px)) * x_scale
+			y0 := @as(f64, Y_MIN) + @itf(@bitcast(py)) * y_scale
 
-	loop if max_iteration >= MAX_MAX_ITERATION break else {
-		py := 0
-		loop if py == screen.height break else {
-			px := 0
-			loop if px >= screen.width break else {
-				x0 := X_MIN + @floatcast(@itf(@bitcast(px))) * x_scale
-				y0 := Y_MIN + @floatcast(@itf(@bitcast(py))) * y_scale
-
-				q := (x0 - 0.25) * (x0 - 0.25) + y0 * y0
-				if q * (q + x0 - 0.25) <= 0.25 * y0 * y0 {
-					px += 1
-					continue
-				}
-
-				if (x0 + 1.0) * (x0 + 1.0) + y0 * y0 <= 0.0625 {
-					px += 1
-					continue
-				}
-
-				x := 0.0
-				y := 0.0
-				iteration := 0
-				x2 := x * x
-				y2 := y * y
-				loop if iteration == max_iteration break else {
-					if x2 + y2 > 4.0 break
-					y = 2.0 * x * y + y0
-					x = x2 - y2 + x0
-					iteration += 1
-					x2 = x * x
-					y2 = y * y
-				}
-
-				smooth_value := @itf(@bitcast(iteration - prev_max_iteration / 10)) / @itf(@bitcast(max_iteration))
-				smooth_value *= smooth_value * (3 - 2 * smooth_value)
-				if iteration < max_iteration & iteration > prev_max_iteration / 10 {
-					colour := render.Color.{
-						r: @intcast(@fti(smooth_value * COLOUR_R)),
-						g: @intcast(@fti(smooth_value * COLOUR_G)),
-						b: @intcast(@fti(smooth_value * COLOUR_B)),
-						a: 0,
-					}
-					screen.put_pixel(.(px, py), colour)
-				}
-				// faster
-				if iteration >= max_iteration {
-					px += 2
-				} else if iteration < 5 {
-					screen.put_hline(py, px, px + 5, .(0, 0, 0, 0))
-					px += 5
-				} else {
-					px += 1
-				}
-				// slower but more slightly more accurate
-				// px += 1
+			q := (x0 - 0.25) * (x0 - 0.25) + y0 * y0
+			if q * (q + x0 - 0.25) <= 0.25 * y0 * y0 {
+				px += 1
+				continue
 			}
-			py += 1
-		}
-		prev_max_iteration = max_iteration
-		max_iteration += 5
-		if USE_SUNSET {
-			_ = sunset.client.send_frame(window)
+
+			if (x0 + 1.0) * (x0 + 1.0) + y0 * y0 <= 0.0625 {
+				px += 1
+				continue
+			}
+
+			x := @as(f64, 0.0)
+			y := @as(f64, 0.0)
+			iteration := 0
+
+			loop if x * x + y * y > @itf(1 << 16) | iteration == MAX_ITERATION break else {
+				x_temp := x * x - y * y + x0
+				y = 2 * x * y + y0
+				x = x_temp
+				iteration += 1
+			}
+
+			c := *screen.indexptr(px, py)
+			if c.r != INTERIOR_COLOUR.r | c.b != INTERIOR_COLOUR.b | c.g != INTERIOR_COLOUR.g {
+				px += 1
+				continue
+			}
+			if iteration < MAX_ITERATION {
+				log_zn := ln_f64(x * x + y * y) / 2.0
+				nu := ln_f64(log_zn / LOG_2) / LOG_2
+				smoothed := @itf(@bitcast(iteration + 1)) - nu
+				smoothed_int := @fti(smoothed)
+				normalised := smoothed - @itf(smoothed_int)
+				colour0 := palette[@bitcast(smoothed_int) % LEN_PALETTE]
+				colour1 := palette[@bitcast(smoothed_int + 1) % LEN_PALETTE]
+				colour := render.Color.{
+					r: @intcast(@fti(lerp_f64(@itf(@intcast(colour0.r)), @itf(@intcast(colour1.r)), normalised))),
+					g: @intcast(@fti(lerp_f64(@itf(@intcast(colour0.g)), @itf(@intcast(colour1.g)), normalised))),
+					b: @intcast(@fti(lerp_f64(@itf(@intcast(colour0.b)), @itf(@intcast(colour1.b)), normalised))),
+					a: 0,
+				}
+				screen.put_pixel(.(px, py), colour)
+			}
+			px += 1
 		}
+		py += 1
+	}
+	if USE_SUNSET loop {
+		_ = sunset.client.send_frame(window)
 	}
 }
\ No newline at end of file
diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml
index 8df697fb..a811525f 100644
--- a/sysdata/system_config.toml
+++ b/sysdata/system_config.toml
@@ -25,11 +25,11 @@ resolution = "1024x768x24"
 [boot.limine.ableos.modules.render_example]
 path = "boot:///render_example.hbf"
 
-[boot.limine.ableos.modules.sunset_server]
-path = "boot:///sunset_server.hbf"
+# [boot.limine.ableos.modules.sunset_server]
+# path = "boot:///sunset_server.hbf"
 
-[boot.limine.ableos.modules.ps2_mouse_driver]
-path = "boot:///ps2_mouse_driver.hbf"
+# [boot.limine.ableos.modules.ps2_mouse_driver]
+# path = "boot:///ps2_mouse_driver.hbf"
 
 # [boot.limine.ableos.modules.ps2_keyboard_driver]
 # path = "boot:///ps2_keyboard_driver.hbf"