Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Circle packing example #72

Merged
merged 4 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions examples/circle_packing.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
open Joy.Shape

(* global constants // RNG initialization *)
let resolution = (1200, 900)
let min_radius = 20
let max_radius = 150
let num_circles = 5_000
let max_attempts = 100_000
let shrink_factor = 0.85
let _ = Stdlib.Random.self_init ()

let palette =
[
(* purple *)
(107, 108, 163);
(* light blue *)
(135, 188, 189);
(* green *)
(111, 153, 84);
(* light purple *)
(150, 155, 199);
(* light green *)
(137, 171, 124);
(* dark purple *)
(67, 68, 117);
(* darker purple *)
(44, 45, 84);
]

(* utility Functions *)

(* distance between two points *)
let distance (x1, y1) (x2, y2) =
let dx = float_of_int x2 -. float_of_int x1 in
let dy = float_of_int y2 -. float_of_int y1 in
let dist = sqrt ((dx *. dx) +. (dy *. dy)) in
int_of_float dist

(* determines if two circles overlaps *)
let overlaps (p1, r1) (p2, r2) =
let dist = distance p1 p2 in
dist < r1 + r2

(* creates a random point within screen bounds *)
let rand_point () =
( Stdlib.Random.full_int (fst resolution * 2) - fst resolution,
Stdlib.Random.full_int (snd resolution * 2) - snd resolution )

(* creates a circle with a random center point and radius *)
let rand_circle () =
let point = rand_point () in
(point, min_radius + Stdlib.Random.full_int (max_radius - min_radius))

(* creates a lis of packed circles *)
let pack_circles () =
(* checks whether a circle intersects with a list of circles *)
let check_overlaps lst current =
List.fold_right (fun curr acc -> overlaps curr current || acc) lst false
in
(* creates a new circle, checks if it intersects previous circles,
if max attempts have been reached,
or if the desired number of circles have been created.
From there it either recurses with or without the new circle,
or returns the list of circles *)
let rec pack lst attempts =
let new_circle = rand_circle () in
let does_overlap = check_overlaps lst new_circle in
let safe = List.length lst < num_circles - 1 && attempts < max_attempts in
if does_overlap && safe then pack lst (attempts + 1)
else if not safe then new_circle :: lst
else pack (new_circle :: lst) attempts
in
let attempts = 0 in
let lst = [ rand_circle () ] in
pack lst attempts

(* pulls a random color from the 'palette' list
sets draw color with it
then draws circle *)
let draw_with_color circle =
let idx = Stdlib.Random.full_int (List.length palette - 1) in
let r, g, b = List.nth palette idx in
Graphics.set_color (Graphics.rgb r g b);
render_shape circle

(* turns a circle into a list of concentric circles *)
let make_concentric circle =
let rec shrink lst =
let point, radius = List.hd (List.rev lst) in
if radius <= 1 then lst
else
let new_circle =
(point, int_of_float (float_of_int radius *. shrink_factor))
in
shrink (lst @ [ new_circle ])
in
shrink [ circle ]

(* main fn *)
let () =
set_dimensions (fst resolution) (snd resolution);
init ();
Graphics.set_line_width 3;
let circles = pack_circles () in
let circles = List.flatten (List.map make_concentric circles) in
List.iter
(fun ((x, y), radius) -> draw_with_color (circle ~x ~y radius))
circles;
close ()
5 changes: 5 additions & 0 deletions examples/dune
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,8 @@
(name complex)
(modules complex)
(libraries joy))

(executable
(name circle_packing)
(modules circle_packing)
(libraries joy))
1 change: 1 addition & 0 deletions lib/shape.mli
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
type point
type shape
type shapes = shape list
type point = { x : int; y : int }

val render_shape : shape -> unit
val point : int -> int -> point
Expand Down
Loading