diff --git a/examples/circle_packing.ml b/examples/circle_packing.ml index ff77867..a954639 100644 --- a/examples/circle_packing.ml +++ b/examples/circle_packing.ml @@ -13,9 +13,6 @@ let num_circles = 10000 (* Maximum attempts at finding new non-overlapping circles *) let max_attempts = 100_000 -(* Factor circles are scaled by *) -let _ = Random.self_init () - let palette = [ (* purple *) @@ -35,12 +32,12 @@ let palette = ] (* utility Functions *) +let rand_nth coll = List.length coll |> random |> List.nth coll let tmap f (a, b) = (f a, f b) -let rand_nth coll = List.length coll |> Random.full_int |> List.nth coll (* Pareto distribution float random for radii *) let pareto scl alpha = - let x = Random.float (1. -. Float.epsilon) +. Float.epsilon in + let x = frandom (1. -. Float.epsilon) +. Float.epsilon in scl *. ((1. -. x) ** (-1. /. alpha)) (* Clamp floats to [min..max] *) @@ -63,7 +60,7 @@ let overlaps (x1, y1, r1) (x2, y2, r2) = (* Creates a random point within screen bounds *) let rand_point () = let w, h = tmap float_of_int size in - (Random.float w -. (w /. 2.), Random.float h -. (h /. 2.)) + (frandom w -. (w /. 2.), frandom h -. (h /. 2.)) (* Creates a circle with a random center point and radius *) let rand_circle () = diff --git a/examples/dune b/examples/dune index 49fd66e..658b600 100644 --- a/examples/dune +++ b/examples/dune @@ -110,8 +110,8 @@ (executable (name flowfield) - (modules flowfield noise) - (libraries joy base)) + (modules flowfield) + (libraries joy)) (executable (name color) diff --git a/examples/flowfield.ml b/examples/flowfield.ml index 6903b4f..d3546c6 100644 --- a/examples/flowfield.ml +++ b/examples/flowfield.ml @@ -3,15 +3,14 @@ let size = 1200 let tau = 2. *. Float.pi let num_steps = 6 let grid_divisor = 128 -let _ = Random.self_init () let octaves = 4 -let noise_scale = 2. +. Random.float 3. +let noise_scale = 2. +. Joy.frandom 3. (* Utilities & color palette *) (* Randomly shuffles a list *) let shuffle xs = - let pairs = List.map (fun c -> (Random.bits (), c)) xs in + let pairs = List.map (fun c -> (Joy.random 0xFFFF, c)) xs in let sorted = List.sort compare pairs in List.map snd sorted @@ -35,12 +34,12 @@ let fclamp max = function f when f > max -> max | f when f < 0. -> 0. | f -> f (* Initialize flowfield, a large 2D array containing angles determined by seeded simplex noise sampled at each coordinate *) let flowfield () = - let seed = Random.float 100. in + let seed = Joy.frandom 100. in Bigarray.Array2.init Bigarray.Float32 Bigarray.c_layout size size (fun x y -> let noise = - Noise.fractal2 octaves - ((float_of_int x /. float_of_int size *. noise_scale) +. seed) - ((float_of_int y /. float_of_int size *. noise_scale) +. seed) + 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)] in let uni = (noise *. 0.5) +. 0.5 in fclamp tau uni *. tau) diff --git a/examples/noise.mli b/examples/noise.mli deleted file mode 100644 index af1455a..0000000 --- a/examples/noise.mli +++ /dev/null @@ -1,12 +0,0 @@ -val permutation : int array -val hash : float -> int -val grad1 : int -> float -> float -val grad2 : int -> float -> float -> float -val snoise1 : float -> float -val snoise2 : float -> float -> float -val frequency : float ref -val amplitude : float ref -val lacunarity : float ref -val persistence : float ref -val fractal1 : int -> float -> float -val fractal2 : int -> float -> float -> float diff --git a/examples/quadtree.ml b/examples/quadtree.ml index a655079..18dfa80 100644 --- a/examples/quadtree.ml +++ b/examples/quadtree.ml @@ -10,9 +10,6 @@ let point_size = 1 let box_color = (0, 0, 0) let point_color = (255, 1, 1) -(* Init rng *) -let _ = Random.self_init () - (* Point utils *) let splat n : point = { x = n; y = n } @@ -27,17 +24,17 @@ let ( /! ) ({ x; y } : point) scalar : point = (* Random utils for creating random clustered points *) let rand_point () : point = - { x = Random.float size -. half_size; y = Random.float size -. half_size } + { 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 offset () = Random.float 100. -. 50. in + let offset () = Joy.frandom 100. -. 50. in center +~ { x = offset (); y = offset () } (* Creates a list of random points clustered around a center point *) let cluster _ = let center = rand_point () in - List.init (8 + Random.int 24) (centered_point center) + List.init (8 + Joy.random 24) (centered_point center) (* Box and utils *) diff --git a/lib/dune b/lib/dune index 5feb774..0c78abd 100644 --- a/lib/dune +++ b/lib/dune @@ -1,5 +1,5 @@ (library (name joy) (public_name joy) - (libraries cairo2) + (libraries cairo2 base) (wrapped false)) diff --git a/lib/joy.ml b/lib/joy.ml index c2f3b4e..d04c4e9 100644 --- a/lib/joy.ml +++ b/lib/joy.ml @@ -1,3 +1,4 @@ +include Random include Shape include Transform include Color @@ -19,3 +20,5 @@ let write ?(filename = "joy.png") () = | None -> Context.fail () let show shapes = Render.show shapes + + diff --git a/lib/joy.mli b/lib/joy.mli index 2037066..5c8caf4 100644 --- a/lib/joy.mli +++ b/lib/joy.mli @@ -22,6 +22,12 @@ 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 fractal_noise : ?octaves:int -> float list -> float + val context : Context.context option ref val set_line_width : int -> unit val black : color diff --git a/examples/noise.ml b/lib/random.ml similarity index 76% rename from examples/noise.ml rename to lib/random.ml index b8c6016..a0623db 100644 --- a/examples/noise.ml +++ b/lib/random.ml @@ -1,4 +1,6 @@ -open Base +(* Internal module for noise *) +module Noise = struct + open Base (* borrowed from https://gist.githubusercontent.com/tjammer/509981fed4d50683cdb800da5bf16ab1/raw/da2b8cc86718ef7e93e2e2c707dcfe443809d7cc/simplex.ml *) @@ -122,3 +124,47 @@ let fractal2 octaves x y = (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 + +(** Ranged integer random, produces a random int in range 0/min to max *) +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 = + 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 + +(** 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 + +(** Sets ref parameters of fractal noise, adjuting the visual character of the noise *) +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 \ No newline at end of file