Skip to content

Commit

Permalink
Wrote basic version of mandelbrot
Browse files Browse the repository at this point in the history
  • Loading branch information
Enoumy committed Dec 12, 2024
1 parent 3e99061 commit 8dbeb2a
Show file tree
Hide file tree
Showing 19 changed files with 443 additions and 0 deletions.
3 changes: 3 additions & 0 deletions app/mandelbrot/bin/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(executable
(name main)
(libraries mandelbrot core_unix.command_unix))
3 changes: 3 additions & 0 deletions app/mandelbrot/bin/main.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
open! Core

let () = Command_unix.run Mandelbrot.command
1 change: 1 addition & 0 deletions app/mandelbrot/bin/main.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(*_ Intentionally left empty. *)
7 changes: 7 additions & 0 deletions app/mandelbrot/src/complex.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open! Core

type t =
{ real : float
; imaginary : float
}
[@@deriving sexp_of]
7 changes: 7 additions & 0 deletions app/mandelbrot/src/complex.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open! Core

type t =
{ real : float
; imaginary : float
}
[@@deriving sexp_of]
92 changes: 92 additions & 0 deletions app/mandelbrot/src/drawing_style.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
open! Core
open! Bonsai
open Capytui
open Bonsai.Let_syntax

type t =
| Basic
| Unicode
| Green
[@@deriving enumerate]

type action =
| Next
| Prev

let component =
let all = Array.of_list all in
let%sub current, inject =
Bonsai.state_machine0
~default_model:0
~apply_action:(fun _ x action ->
let offset = match action with Prev -> -1 | Next -> 1 in
(x + offset) % Array.length all)
()
in
let%arr current = current
and inject = inject in
all.(current), inject
;;

let iter_on_pixels result ~f =
let rows =
Array.to_list result
|> List.map ~f:(fun row ->
let columns = Array.to_list row |> List.map ~f in
Node.hcat columns)
in
Node.vcat rows
;;

let draw t =
let%sub text = Text.component in
let%sub flavor = Capytui_catpuccin.flavor in
match%sub t with
| Basic ->
let%arr text = text in
fun ~max_iterations result ->
let string =
String.concat_array ~sep:"\n"
@@ Array.map result ~f:(fun line ->
String.concat_array
@@ Array.map line ~f:(fun iteration ->
if iteration > max_iterations / 2 then "X" else "."))
in
Node.vcat (String.split ~on:'\n' string |> List.map ~f:text)
| Unicode ->
let%arr text = text
and flavor = flavor in
fun ~max_iterations result ->
iter_on_pixels result ~f:(fun iteration ->
if iteration > max_iterations / 2
then
text
~attrs:
[ Attr.background_color (Capytui_catpuccin.color ~flavor Text)
]
" "
else text " ")
| Green ->
let%arr text = text in
fun ~max_iterations result ->
iter_on_pixels result ~f:(fun iteration ->
let quotient =
Float.of_int iteration /. Float.of_int max_iterations
in
let color =
Render.lerp 0.0 255.0 (Percent.of_mult quotient) |> Int.of_float
in
if iteration > max_iterations / 2
then
text
~attrs:
[ Attr.background_color
(Attr.Color.rgb ~r:color ~g:255 ~b:color)
]
" "
else
text
~attrs:
[ Attr.background_color (Attr.Color.rgb ~r:0 ~g:color ~b:0) ]
" ")
;;
19 changes: 19 additions & 0 deletions app/mandelbrot/src/drawing_style.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
open! Core
open Bonsai
open Capytui

type t =
| Basic
| Unicode
| Green
[@@deriving enumerate]

type action =
| Next
| Prev

val component : (t * (action -> unit Effect.t)) Computation.t

val draw
: t Value.t
-> (max_iterations:int -> int array array -> Node.t) Computation.t
5 changes: 5 additions & 0 deletions app/mandelbrot/src/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(library
(name mandelbrot)
(libraries core bonsai capytui async capytui_catpuccin)
(preprocess
(pps ppx_jane bonsai.ppx_bonsai)))
130 changes: 130 additions & 0 deletions app/mandelbrot/src/mandelbrot.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
open! Core
open Async
open! Bonsai
open! Capytui
open! Bonsai.Let_syntax

module View_port = struct
type t =
{ center : Complex.t
; scale : Percent.t
; max_iterations : int
}

type action =
| Zoom_in
| Zoom_out
| Up
| Down
| Left
| Right
| Increase_iterations
| Decreate_iterations

let default =
let center = { Complex.real = 0.0; imaginary = 0.0 }
and scale = Percent.one_hundred_percent
and max_iterations = 1_000 in
{ center; scale; max_iterations }
;;

let apply_action _ { center; scale; max_iterations } action =
let center =
let multiple = 0.1 *. Percent.to_mult scale in
match action with
| Zoom_in | Zoom_out | Increase_iterations | Decreate_iterations ->
center
| Up -> { center with imaginary = center.imaginary -. multiple }
| Down -> { center with imaginary = center.imaginary +. multiple }
| Left -> { center with real = center.real -. multiple }
| Right -> { center with real = center.real +. multiple }
and scale =
match action with
| Zoom_in -> Percent.( * ) scale (Percent.of_mult 0.9)
| Zoom_out -> Percent.( * ) scale (Percent.of_mult (1.0 /. 0.9))
| Up | Down | Left | Right | Increase_iterations | Decreate_iterations
->
scale
and max_iterations =
match action with
| Increase_iterations -> max_iterations + 100
| Decreate_iterations -> Int.max 100 (max_iterations - 100)
| _ -> max_iterations
in
{ center; scale; max_iterations }
;;

let component =
Bonsai.state_machine0 ~default_model:default ~apply_action ()
;;
end

let backdrop =
let%sub dimensions = Capytui.terminal_dimensions in
let%sub flavor = Capytui_catpuccin.flavor in
let%arr { height; width } = dimensions
and flavor = flavor in
Node.vcat
(List.init height ~f:(fun _ ->
Node.text
~attrs:
[ Attr.background_color (Capytui_catpuccin.color ~flavor Crust) ]
(String.make width ' ')))
;;

let app =
let%sub dimensions = Capytui.terminal_dimensions in
let%sub { center; scale; max_iterations }, inject = View_port.component in
let%sub drawing_style, inject_drawing_style = Drawing_style.component in
let%sub handler =
let%arr inject = inject
and inject_drawing_style = inject_drawing_style in
fun (event : Event.t) ->
match event with
| `Key (`ASCII '+', []) -> inject Zoom_in
| `Key (`ASCII '-', []) -> inject Zoom_out
| `Key (`ASCII 'j', []) -> inject Down
| `Key (`ASCII 'k', []) -> inject Up
| `Key (`ASCII 'h', []) -> inject Left
| `Key (`ASCII 'l', []) -> inject Right
| `Key (`ASCII 'u', []) -> inject Increase_iterations
| `Key (`ASCII 'd', []) -> inject Decreate_iterations
| `Key (`ASCII 'n', []) -> inject_drawing_style Next
| `Key (`ASCII 'N', []) -> inject_drawing_style Prev
| _ -> Effect.Ignore
in
let%sub () = Capytui.listen_to_events handler in
let%sub result =
let%arr dimensions = dimensions
and center = center
and scale = scale
and max_iterations = max_iterations in
Render.render ~center ~scale ~max_iterations ~dimensions
in
let%sub draw = Drawing_style.draw drawing_style in
let%sub rendered =
let%arr result = result
and draw = draw
and max_iterations = max_iterations in
draw ~max_iterations result
in
let%sub backdrop = backdrop in
let%arr rendered = rendered
and backdrop = backdrop in
Node.zcat [ rendered; backdrop ]
;;

let command =
Command.async_or_error
~summary:{|A small mandelbrot demo.|}
[%map_open.Command
let () = return () in
fun () ->
let open Deferred.Or_error.Let_syntax in
let%bind () = Capytui.start app in
return ()]
;;

module For_testing = struct
module Render = Render
end
7 changes: 7 additions & 0 deletions app/mandelbrot/src/mandelbrot.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open! Core

val command : Command.t

module For_testing : sig
module Render = Render
end
45 changes: 45 additions & 0 deletions app/mandelbrot/src/render.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
open! Core
open Capytui

let lerp v0 v1 t =
let t = Percent.to_mult t in
v0 +. (t *. (v1 -. v0))
;;

let render
~center
~scale
~max_iterations
~dimensions:({ width; height } : Dimensions.t)
=
let center =
{ Complex.real = center.Complex.real -. 0.765
; imaginary = center.imaginary
}
in
let left = center.real -. (1.235 *. Percent.to_mult scale)
and right = center.real +. (1.235 *. Percent.to_mult scale)
and top = center.imaginary -. (1.12 *. Percent.to_mult scale)
and bottom = center.imaginary +. (1.12 *. Percent.to_mult scale) in
Array.init height ~f:(fun y ->
let y = Int.to_float y
and height = Int.to_float height in
Array.init width ~f:(fun x ->
let x = Int.to_float x
and width = Int.to_float width in
let x0 = lerp left right (Percent.of_mult (x /. width))
and y0 = lerp top bottom (Percent.of_mult (y /. height)) in
let x = ref 0.0
and y = ref 0.0
and iteration = ref 0 in
let ( <= ) = Float.( <= ) in
while
(!x *. !x) +. (!y *. !y) <= 2.0 *. 2.0 && !iteration < max_iterations
do
let xtemp = (!x *. !x) -. (!y *. !y) +. x0 in
y := (2.0 *. !x *. !y) +. y0;
x := xtemp;
incr iteration
done;
!iteration))
;;
11 changes: 11 additions & 0 deletions app/mandelbrot/src/render.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
open! Core
open Capytui

val lerp : float -> float -> Percent.t -> float

val render
: center:Complex.t
-> scale:Percent.t
-> max_iterations:int
-> dimensions:Dimensions.t
-> int array array
15 changes: 15 additions & 0 deletions app/mandelbrot/src/text.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
open! Core
open Bonsai.Let_syntax
open Capytui

let component =
let%sub flavor = Capytui_catpuccin.flavor in
let%arr flavor = flavor in
let crust = Capytui_catpuccin.color ~flavor Crust in
let text = Capytui_catpuccin.color ~flavor Text in
fun ?(attrs = []) string ->
Node.text
~attrs:
([ Attr.background_color crust; Attr.foreground_color text ] @ attrs)
string
;;
5 changes: 5 additions & 0 deletions app/mandelbrot/src/text.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
open! Core
open Bonsai
open Capytui

val component : (?attrs:Attr.t list -> string -> Node.t) Computation.t
1 change: 1 addition & 0 deletions app/mandelbrot/src/ultra_fractal.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
open! Core
1 change: 1 addition & 0 deletions app/mandelbrot/src/ultra_fractal.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
open! Core
6 changes: 6 additions & 0 deletions app/mandelbrot/test/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(library
(name test_mandelbrot)
(libraries mandelbrot core bonsai capytui)
(inline_tests)
(preprocess
(pps ppx_jane)))
Loading

0 comments on commit 8dbeb2a

Please sign in to comment.