Skip to content

Commit

Permalink
fix ellipse control points && fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
FayCarsons committed Mar 8, 2024
1 parent 9ffa611 commit 4b08b74
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 171 deletions.
2 changes: 1 addition & 1 deletion examples/dune
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
(modules color)
(libraries joy))

(executable
(executable
(name fill_rect)
(modules fill_rect)
(libraries joy))
8 changes: 5 additions & 3 deletions examples/flowfield.ml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ let tau = 2. *. Float.pi
let num_steps = 6
let grid_divisor = 128
let octaves = 4
let noise_scale = 2. +. Joy.frandom 3.
let noise_scale = 2. +. Joy.frandom 3.

(* Utilities & color palette *)

Expand Down Expand Up @@ -38,8 +38,10 @@ let flowfield () =
Bigarray.Array2.init Bigarray.Float32 Bigarray.c_layout size size (fun x y ->
let noise =
Joy.fractal_noise ~octaves
[((float_of_int x /. float_of_int size *. noise_scale) +. seed);
((float_of_int y /. float_of_int size *. noise_scale) +. seed)]
[
(float_of_int x /. float_of_int size *. noise_scale) +. seed;
(float_of_int y /. float_of_int size *. noise_scale) +. seed;
]
in
let uni = (noise *. 0.5) +. 0.5 in
fclamp tau uni *. tau)
Expand Down
2 changes: 1 addition & 1 deletion examples/quadtree.ml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ let rand_point () : point =
{ x = Joy.frandom size -. half_size; y = Joy.frandom size -. half_size }

(* Creates a point within 50 units of a center *)
let centered_point (center : point) _: point =
let centered_point (center : point) _ : point =
let offset () = Joy.frandom 100. -. 50. in
center +~ { x = offset (); y = offset () }

Expand Down
2 changes: 0 additions & 2 deletions lib/joy.ml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,3 @@ let write ?(filename = "joy.png") () =
| None -> Context.fail ()

let show shapes = Render.show shapes


8 changes: 3 additions & 5 deletions lib/joy.mli
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@ val compose : transformation -> transformation -> transformation
val repeat : int -> transformation -> transformation
val map_stroke : (color -> color) -> shape -> shape
val map_fill : (color -> color) -> shape -> shape

val random : ?min:int -> int -> int
val frandom : ?min:float -> float -> float
val noise : float list -> float
val random : ?min:int -> int -> int
val frandom : ?min:float -> float -> float
val noise : float list -> float
val fractal_noise : ?octaves:int -> float list -> float

val context : Context.context option ref
val set_line_width : int -> unit
val black : color
Expand Down
284 changes: 141 additions & 143 deletions lib/random.ml
Original file line number Diff line number Diff line change
@@ -1,170 +1,168 @@
(* Internal module for noise *)
module Noise = struct
module Noise = struct
open Base

(* borrowed from
https://gist.githubusercontent.com/tjammer/509981fed4d50683cdb800da5bf16ab1/raw/da2b8cc86718ef7e93e2e2c707dcfe443809d7cc/simplex.ml *)
let permutation =
[|
151; 160; 137; 91; 90; 15; 131; 13; 201; 95; 96; 53; 194; 233; 7; 225; 140;
36; 103; 30; 69; 142; 8; 99; 37; 240; 21; 10; 23; 190; 6; 148; 247; 120;
234; 75; 0; 26; 197; 62; 94; 252; 219; 203; 117; 35; 11; 32; 57; 177; 33;
88; 237; 149; 56; 87; 174; 20; 125; 136; 171; 168; 68; 175; 74; 165; 71;
134; 139; 48; 27; 166; 77; 146; 158; 231; 83; 111; 229; 122; 60; 211; 133;
230; 220; 105; 92; 41; 55; 46; 245; 40; 244; 102; 143; 54; 65; 25; 63; 161;
1; 216; 80; 73; 209; 76; 132; 187; 208; 89; 18; 169; 200; 196; 135; 130;
116; 188; 159; 86; 164; 100; 109; 198; 173; 186; 3; 64; 52; 217; 226; 250;
124; 123; 5; 202; 38; 147; 118; 126; 255; 82; 85; 212; 207; 206; 59; 227;
47; 16; 58; 17; 182; 189; 28; 42; 223; 183; 170; 213; 119; 248; 152; 2; 44;
154; 163; 70; 221; 153; 101; 155; 167; 43; 172; 9; 129; 22; 39; 253; 19; 98;
108; 110; 79; 113; 224; 232; 178; 185; 112; 104; 218; 246; 97; 228; 251; 34;
242; 193; 238; 210; 144; 12; 191; 179; 162; 241; 81; 51; 145; 235; 249; 14;
239; 107; 49; 192; 214; 31; 181; 199; 106; 157; 184; 84; 204; 176; 115; 121;
50; 45; 127; 4; 150; 254; 138; 236; 205; 93; 222; 114; 67; 29; 24; 72; 243;
141; 128; 195; 78; 66; 215; 61; 156; 180;
|]
[@@ocamlformat "break-collection-expressions=wrap"]

let hash n = permutation.(Int.of_float n land 255)

let grad1 hash x =
let h = hash land 0x0F in
(* gradient value 1.0, 2.0 .. 8.0 *)
let grad = 1.0 +. Float.of_int (h land 7) in
let grad = if h land 8 <> 0 then -.grad else grad in
grad *. x

let grad2 hash x y =
let h = hash land 0x3F in
let u, v = if h < 4 then (x, y) else (y, x) in
(if h land 1 <> 0 then -.u else u)
+. if h land 2 <> 0 then -2.0 *. v else 2.0 *. v

let snoise1 x =
let i0 = Float.round_down x in
let i1 = i0 +. 1.0 in
let x0 = x -. i0 in
let x1 = x0 -. 1.0 in
let t0 = 1.0 -. (x0 *. x0) in
let t0 = t0 *. t0 in
let n0 = t0 *. t0 *. grad1 (hash i0) x0 in
let t1 = 1.0 -. (x1 *. x1) in
let t1 = t1 *. t1 in
let n1 = t1 *. t1 *. grad1 (hash i1) x1 in
(* The maximum value of this noise is 8*(3/4)^4 = 2.53125 *)
(* A factor of 0.395 scales to fit exactly within [-1,1] *)
0.395 *. (n0 +. n1)

let snoise2 x y =
let _F2 = 0.366025403 in
(* F2 = (sqrt(3) - 1) / 2*)
let _G2 = 0.211324865 in
(*G2 = (3 - sqrt(3)) / 6 = F2 / (1 + 2 * K)*)
(* skew the input space to determine which simplex cell we're in *)
let s = (x +. y) *. _F2 in
let xs, ys = (x +. s, y +. s) in
let i, j = Float.(round_down xs, round_down ys) in
(* unskew the cell origin back to (x, y) space *)
let t = (i +. j) *. _G2 in
let _X0 = i -. t in
let _Y0 = j -. t in
let x0, y0 = (x -. _X0, y -. _Y0) in
(* determine which simplex we're in *)
let i1, j1 = if Poly.(x0 > y0) then (1., 0.) else (0., 1.) in
(* A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and *)
(* a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where *)
(* c = (3-sqrt(3))/6 *)
let x1, y1 = Float.(x0 - i1 + _G2, y0 - j1 + _G2) in
let x2, y2 = Float.(x0 - 1.0 + (2.0 * _G2), y0 - 1.0 + (2.0 * _G2)) in
(* Work out the hashed gradient indices of the three simplex corners *)
let gi0 = (j |> hash |> Float.of_int) +. i |> hash in
let gi1 = (j +. j1 |> hash |> Float.of_int) +. i +. i1 |> hash in
let gi2 = (j +. 1. |> hash |> Float.of_int) +. i +. 1. |> hash in
let contrib x y gi =
let t = 0.5 -. (x *. x) -. (y *. y) in
if Float.(t < 0.0) then 0.0
else
let t = t *. t in
t *. t *. grad2 gi x y
in
(* Calculate the contribution from the first corner *)
let n0 = contrib x0 y0 gi0 in
(* Calculate the contribution from the second corner *)
let n1 = contrib x1 y1 gi1 in
(* Calculate the contribution from the third corner *)
let n2 = contrib x2 y2 gi2 in
45.23065 *. (n0 +. n1 +. n2)

(* constants *)
let frequency = ref 1.0
let amplitude = ref 1.0
let lacunarity = ref 2.0
let persistence = ref 0.5

let fractal1 octaves x =
let rec loop noise amp i =
if i = 0 then noise /. amp
else
let frequency = !frequency *. Float.int_pow !lacunarity (i - 1) in
let amplitude = !amplitude *. Float.int_pow !persistence (i - 1) in
loop
(noise +. (amplitude *. snoise1 (x *. frequency)))
(amp +. amplitude) (i - 1)
in
loop 0.0 0.0 octaves

let fractal2 octaves x y =
let rec loop noise amp i =
if i = 0 then noise /. amp
else
let frequency = !frequency *. Float.int_pow !lacunarity (i - 1) in
let amplitude = !amplitude *. Float.int_pow !persistence (i - 1) in
loop
(noise +. (amplitude *. snoise2 (x *. frequency) (y *. frequency)))
(amp +. amplitude) (i - 1)
in
loop 0.0 0.0 octaves
(* borrowed from
https://gist.githubusercontent.com/tjammer/509981fed4d50683cdb800da5bf16ab1/raw/da2b8cc86718ef7e93e2e2c707dcfe443809d7cc/simplex.ml *)
let permutation =
[|
151; 160; 137; 91; 90; 15; 131; 13; 201; 95; 96; 53; 194; 233; 7; 225;
140; 36; 103; 30; 69; 142; 8; 99; 37; 240; 21; 10; 23; 190; 6; 148; 247;
120; 234; 75; 0; 26; 197; 62; 94; 252; 219; 203; 117; 35; 11; 32; 57; 177;
33; 88; 237; 149; 56; 87; 174; 20; 125; 136; 171; 168; 68; 175; 74; 165;
71; 134; 139; 48; 27; 166; 77; 146; 158; 231; 83; 111; 229; 122; 60; 211;
133; 230; 220; 105; 92; 41; 55; 46; 245; 40; 244; 102; 143; 54; 65; 25;
63; 161; 1; 216; 80; 73; 209; 76; 132; 187; 208; 89; 18; 169; 200; 196;
135; 130; 116; 188; 159; 86; 164; 100; 109; 198; 173; 186; 3; 64; 52; 217;
226; 250; 124; 123; 5; 202; 38; 147; 118; 126; 255; 82; 85; 212; 207; 206;
59; 227; 47; 16; 58; 17; 182; 189; 28; 42; 223; 183; 170; 213; 119; 248;
152; 2; 44; 154; 163; 70; 221; 153; 101; 155; 167; 43; 172; 9; 129; 22;
39; 253; 19; 98; 108; 110; 79; 113; 224; 232; 178; 185; 112; 104; 218;
246; 97; 228; 251; 34; 242; 193; 238; 210; 144; 12; 191; 179; 162; 241;
81; 51; 145; 235; 249; 14; 239; 107; 49; 192; 214; 31; 181; 199; 106; 157;
184; 84; 204; 176; 115; 121; 50; 45; 127; 4; 150; 254; 138; 236; 205; 93;
222; 114; 67; 29; 24; 72; 243; 141; 128; 195; 78; 66; 215; 61; 156; 180;
|]
[@@ocamlformat "break-collection-expressions=wrap"]

let hash n = permutation.(Int.of_float n land 255)

let grad1 hash x =
let h = hash land 0x0F in
(* gradient value 1.0, 2.0 .. 8.0 *)
let grad = 1.0 +. Float.of_int (h land 7) in
let grad = if h land 8 <> 0 then -.grad else grad in
grad *. x

let grad2 hash x y =
let h = hash land 0x3F in
let u, v = if h < 4 then (x, y) else (y, x) in
(if h land 1 <> 0 then -.u else u)
+. if h land 2 <> 0 then -2.0 *. v else 2.0 *. v

let snoise1 x =
let i0 = Float.round_down x in
let i1 = i0 +. 1.0 in
let x0 = x -. i0 in
let x1 = x0 -. 1.0 in
let t0 = 1.0 -. (x0 *. x0) in
let t0 = t0 *. t0 in
let n0 = t0 *. t0 *. grad1 (hash i0) x0 in
let t1 = 1.0 -. (x1 *. x1) in
let t1 = t1 *. t1 in
let n1 = t1 *. t1 *. grad1 (hash i1) x1 in
(* The maximum value of this noise is 8*(3/4)^4 = 2.53125 *)
(* A factor of 0.395 scales to fit exactly within [-1,1] *)
0.395 *. (n0 +. n1)

let snoise2 x y =
let _F2 = 0.366025403 in
(* F2 = (sqrt(3) - 1) / 2*)
let _G2 = 0.211324865 in
(*G2 = (3 - sqrt(3)) / 6 = F2 / (1 + 2 * K)*)
(* skew the input space to determine which simplex cell we're in *)
let s = (x +. y) *. _F2 in
let xs, ys = (x +. s, y +. s) in
let i, j = Float.(round_down xs, round_down ys) in
(* unskew the cell origin back to (x, y) space *)
let t = (i +. j) *. _G2 in
let _X0 = i -. t in
let _Y0 = j -. t in
let x0, y0 = (x -. _X0, y -. _Y0) in
(* determine which simplex we're in *)
let i1, j1 = if Poly.(x0 > y0) then (1., 0.) else (0., 1.) in
(* A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and *)
(* a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where *)
(* c = (3-sqrt(3))/6 *)
let x1, y1 = Float.(x0 - i1 + _G2, y0 - j1 + _G2) in
let x2, y2 = Float.(x0 - 1.0 + (2.0 * _G2), y0 - 1.0 + (2.0 * _G2)) in
(* Work out the hashed gradient indices of the three simplex corners *)
let gi0 = (j |> hash |> Float.of_int) +. i |> hash in
let gi1 = (j +. j1 |> hash |> Float.of_int) +. i +. i1 |> hash in
let gi2 = (j +. 1. |> hash |> Float.of_int) +. i +. 1. |> hash in
let contrib x y gi =
let t = 0.5 -. (x *. x) -. (y *. y) in
if Float.(t < 0.0) then 0.0
else
let t = t *. t in
t *. t *. grad2 gi x y
in
(* Calculate the contribution from the first corner *)
let n0 = contrib x0 y0 gi0 in
(* Calculate the contribution from the second corner *)
let n1 = contrib x1 y1 gi1 in
(* Calculate the contribution from the third corner *)
let n2 = contrib x2 y2 gi2 in
45.23065 *. (n0 +. n1 +. n2)

(* constants *)
let frequency = ref 1.0
let amplitude = ref 1.0
let lacunarity = ref 2.0
let persistence = ref 0.5

let fractal1 octaves x =
let rec loop noise amp i =
if i = 0 then noise /. amp
else
let frequency = !frequency *. Float.int_pow !lacunarity (i - 1) in
let amplitude = !amplitude *. Float.int_pow !persistence (i - 1) in
loop
(noise +. (amplitude *. snoise1 (x *. frequency)))
(amp +. amplitude) (i - 1)
in
loop 0.0 0.0 octaves

let fractal2 octaves x y =
let rec loop noise amp i =
if i = 0 then noise /. amp
else
let frequency = !frequency *. Float.int_pow !lacunarity (i - 1) in
let amplitude = !amplitude *. Float.int_pow !persistence (i - 1) in
loop
(noise +. (amplitude *. snoise2 (x *. frequency) (y *. frequency)))
(amp +. amplitude) (i - 1)
in
loop 0.0 0.0 octaves
end

(* Library exposed to users *)

let initialized = ref false

let initialize () = Stdlib.Random.self_init (); initialized := true
let initialize () =
Stdlib.Random.self_init ();
initialized := true

(** Ranged integer random, produces a random int in range 0/min to max *)
let random ?(min = 0) max =
if not !initialized then initialize ();
let random ?(min = 0) max =
if not !initialized then initialize ();
min + Stdlib.Random.int (max - min)

(** Ranged float random, produces a random float in range 0./min to max *)
let frandom ?(min = 0.) max =
let frandom ?(min = 0.) max =
if not !initialized then initialize ();
min +. Stdlib.Random.float (max -. min)

(** Simplex noise, output in range -1..1 *)
let noise = function
| [] ->
print_endline "Noise noise requires 1-2 elements in arg list";
0.
| [ x ] ->
Noise.snoise1 x
| x :: y :: _ ->
Noise.snoise2 x y
| [] ->
print_endline "Noise noise requires 1-2 elements in arg list";
0.
| [ x ] -> Noise.snoise1 x
| x :: y :: _ -> Noise.snoise2 x y

(** Layered 'fractal' noise, output in range -1..1 *)
let fractal_noise ?(octaves = 5) = function
| [] ->
print_endline "Fractal noise requires 1-2 elements in arg list";
0.
| [ x ] ->
Noise.fractal1 octaves x
| x :: y :: _ ->
Noise.fractal2 octaves x y
| [] ->
print_endline "Fractal noise requires 1-2 elements in arg list";
0.
| [ x ] -> Noise.fractal1 octaves x
| x :: y :: _ -> Noise.fractal2 octaves x y

(** Sets ref parameters of fractal noise, adjuting the visual character of the noise *)
let set_fractal_params ?frequency ?amplitude ?lacunarity ?persistence () =
let set_fractal_params ?frequency ?amplitude ?lacunarity ?persistence () =
Option.iter (fun n -> Noise.frequency := n) frequency;
Option.iter (fun n -> Noise.amplitude := n) amplitude;
Option.iter (fun n -> Noise.lacunarity := n) lacunarity;
Option.iter (fun n -> Noise.persistence := n) persistence
Option.iter (fun n -> Noise.persistence := n) persistence
Loading

0 comments on commit 4b08b74

Please sign in to comment.