-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactoring for clustering behavior + fmt
- Loading branch information
1 parent
304db46
commit 00f077b
Showing
8 changed files
with
117 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,107 +1,124 @@ | ||
open Joy | ||
|
||
type point = Joy.point | ||
|
||
(* Constants *) | ||
let size = 800. | ||
let half_size = size /. 2. | ||
let max_leaf_points = 3 | ||
let num_points = 900 | ||
let max_leaf_points = 4 | ||
let clusters = 32 | ||
|
||
(* Init rng *) | ||
let _ = Random.self_init () | ||
|
||
(* Point utils *) | ||
let pmap2 f ({x = x1; y = y1}: point) ({ x = x2; y = y2}: point): point = { x = f x1 x2; y = f y1 y2} | ||
let ( /! ) ({ x; y}: point) scalar: point = { x = x /. scalar; y = y /. scalar } | ||
let splat n = point n n | ||
|
||
let pmap2 f ({ x = x1; y = y1 } : point) ({ x = x2; y = y2 } : point) = | ||
point (f x1 x2) (f y1 y2) | ||
|
||
let ( +~ ) (p1 : point) (p2 : point) : point = | ||
point (p1.x +. p2.x) (p1.y +. p2.y) | ||
|
||
let ( /! ) ({ x; y } : point) scalar : point = | ||
{ x = x /. scalar; y = y /. scalar } | ||
|
||
let rand_point _: point = { x = (Random.float size) -. half_size; y = (Random.float size) -. half_size} | ||
(* Random utils for creating random clustered points *) | ||
let rand_point () = | ||
point (Random.float size -. half_size) (Random.float size -. half_size) | ||
|
||
let centered_point (center : point) _ : point = | ||
let offset () = Random.float 100. -. 50. in | ||
center +~ { x = offset (); y = offset () } | ||
|
||
let cluster _ = | ||
let center = rand_point () in | ||
List.init (8 + Random.int 24) (centered_point center) | ||
|
||
(* Box and utils *) | ||
type box = { min : point; max: point } | ||
let box (minx, miny) (maxx, maxy) = | ||
{ min = { x = minx; y = miny }; max = { x = maxx; y = maxy }} | ||
|
||
let midpoint { min; max } = | ||
(pmap2 (+.) min max) /! 2. | ||
|
||
let quarters (mid: point) box = | ||
let lu = { min = { x = box.min.x; y = mid.y }; max = { x = mid.x; y = box.max.y } } in | ||
let ru = { min = { x = mid.x; y = mid.y }; max = { x = box.max.x; y = box.max.y } } in | ||
let rd = { min = { x = mid.x; y = box.min.y }; max = { x = box.max.x; y = mid.y } } in | ||
let ld = { min = box.min; max = mid } in | ||
(lu, ru, rd, ld) | ||
type box = { min : point; max : point } | ||
(** Axis aligned bounding box *) | ||
|
||
(* let (|=) box ({x; y}: point) = | ||
{ min = { x = min box.min.x x; y = min box.min.y y }; max = { x = max box.max.x x; y = max box.max.y y }} | ||
let box min max = { min; max } | ||
|
||
let bound_points = List.fold_left (fun acc p -> acc |= p) *) | ||
(** Returns the middle point of the box *) | ||
let midpoint { min; max } = pmap2 ( +. ) min max /! 2. | ||
|
||
let contains box ({ x;y }: point) = | ||
x > box.min.x && x < box.max.x && y > box.min.y && y < box.max.y | ||
(** Subdivides a box into four even axis-aligned boxes *) | ||
let quarters ({ min; max } as box) = | ||
let mid = midpoint box in | ||
let lu = { min = { x = min.x; y = mid.y }; max = { x = mid.x; y = max.y } } in | ||
let ru = { min = { x = mid.x; y = mid.y }; max = { x = max.x; y = max.y } } in | ||
let rd = { min = { x = mid.x; y = min.y }; max = { x = max.x; y = mid.y } } in | ||
let ld = { min; max = mid } in | ||
(lu, ru, rd, ld) | ||
|
||
(** checks whether point is within bounds of box *) | ||
let contains { min; max } ({ x; y } : point) = | ||
x > min.x && x < max.x && y > min.y && y < max.y | ||
|
||
(* Quadtree and utils *) | ||
type 'a leaf = box * 'a list | ||
type 'a tree = Empty | Leaf of 'a leaf | Node of { aabb : box; children : 'a tree list } | ||
|
||
let split_root box points = | ||
let partition (lu, ru, rd, ld) es = | ||
let belong box = List.filter (contains box) in | ||
( | ||
(lu, (belong lu es)), | ||
(ru, (belong ru es)), | ||
(rd, (belong rd es)), | ||
(ld, (belong ld es)) | ||
) | ||
(** Leaf is 2-tuple of bounding box * 'a list of elts whose position is within that box *) | ||
|
||
type 'a tree = Leaf of 'a leaf | Node of 'a tree list | ||
(* Node potentially doesn't need to hold aabb? *) | ||
|
||
(** Constructs tree from root *) | ||
let split_root box points = | ||
(* Groups points with the boxes that contain them *) | ||
let partition (lu, ru, rd, ld) es = | ||
let belongs box = List.filter (contains box) in | ||
( (lu, belongs lu es), | ||
(ru, belongs ru es), | ||
(rd, belongs rd es), | ||
(ld, belongs ld es) ) | ||
in | ||
(* Splits and converts to Node if leaf has too many points, | ||
otherwise returns leaf *) | ||
let rec split (box, es) = | ||
if List.length es > max_leaf_points then | ||
let quarters' = quarters box in | ||
let lu, ru, rd, ld = partition quarters' points in | ||
Node (List.map split [ lu; ru; rd; ld ]) | ||
else Leaf (box, es) | ||
in | ||
let rec split (box, es) = | ||
if List.length es > max_leaf_points then | ||
let mid = midpoint box in | ||
let quarters' = quarters mid box in | ||
let (lu, ru, rd,ld) = partition quarters' points in | ||
Node { aabb = box; children = List.map split [lu; ru; rd; ld]} | ||
else | ||
Leaf (box, es) | ||
in | ||
split (box, points) | ||
|
||
|
||
let build () = | ||
let _ = Empty in | ||
let root = box ((-.half_size), (-. half_size)) (half_size, half_size) in | ||
let points = List.init num_points rand_point in | ||
let build () = | ||
let root = box (splat (-.half_size)) (splat half_size) in | ||
let points = List.flatten (List.init clusters cluster) in | ||
split_root root points | ||
|
||
let to_flat_shapes tree: shape list = | ||
let rect_of_bb bb = rectangle ~c:(midpoint bb) (bb.max.x -. bb.min.x) (bb.max.y -. bb.min.y) in | ||
let circle_of_point pt = | ||
circle ~c:pt 1. | ||
let to_flat_shapes tree = | ||
let rect_of_bb bb = | ||
rectangle ~c:(midpoint bb) (bb.max.x -. bb.min.x) (bb.max.y -. bb.min.y) | ||
in | ||
let rec convert xs = function | ||
| Node { aabb; children } -> | ||
let b = rect_of_bb aabb in | ||
List.flatten (List.map (convert (b :: xs)) children) | ||
| Leaf (aabb, es) -> | ||
let b = rect_of_bb aabb in | ||
((List.map circle_of_point es) @ (b :: xs) ) | ||
| Empty -> | ||
[] | ||
let circle_of_point pt = circle ~c:pt 1. in | ||
let rec convert xs = function | ||
| Node children -> List.flatten (List.map (convert xs) children) | ||
| Leaf (aabb, es) -> | ||
let b = rect_of_bb aabb in | ||
List.map circle_of_point es @ (b :: xs) | ||
in | ||
convert [] tree | ||
|
||
(* With color handling system this function won't be necessary as color can be | ||
decided at construction *) | ||
let render_color shape = | ||
match shape with | ||
| Shape.Circle _ -> | ||
match shape with | ||
| Shape.Circle _ -> | ||
set_color (1., 1. /. 255., 1. /. 255.); | ||
render shape | ||
| _ -> | ||
| _ -> | ||
set_color (0., 0., 0.); | ||
render shape | ||
render shape | ||
|
||
let () = | ||
let () = | ||
init (); | ||
background (1., 1., 1., 1.); | ||
let tree = build () in | ||
let to_shapes = to_flat_shapes tree in | ||
let tree = build () in | ||
let to_shapes = to_flat_shapes tree in | ||
set_color (0., 0., 0.); | ||
List.iter render_color to_shapes; | ||
write ~filename:"quadtree.png" () | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
val translate : float -> float -> Shape.shape -> Shape.shape | ||
val scale : float -> Shape.shape -> Shape.shape | ||
val rotate : int -> Shape.shape -> Shape.shape | ||
val compose : (Shape.shape -> Shape.shape) -> (Shape.shape -> Shape.shape) -> Shape.shape -> Shape.shape | ||
val repeat : int -> (Shape.shape -> Shape.shape) -> Shape.shape -> Shape.shape | ||
|
||
val compose : | ||
(Shape.shape -> Shape.shape) -> | ||
(Shape.shape -> Shape.shape) -> | ||
Shape.shape -> | ||
Shape.shape | ||
|
||
val repeat : int -> (Shape.shape -> Shape.shape) -> Shape.shape -> Shape.shape |