package grenier
A collection of various algorithms in OCaml
Install
Dune Dependency
Authors
Maintainers
Sources
grenier-0.15.tbz
sha256=dec7f84b9e93d5825f10c7dea84d5a74d7365ede45664ae63c26b5e8045c1c44
sha512=b8aa1569c2e24b89674d1b34de34cd1798896bb6a53aa5a1287f68cee880125e6b687f66ad73da9069a01cc3ece1f0684f48328b099d43529bff736b772c8fd8
doc/src/grenier.binpacking/maxrects.ml.html
Source file maxrects.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
type 'bin bin = { bin_x : int; bin_y : int; bin_w : int; bin_h : int; bin_root : 'bin bin; bin_tag : 'bin; } type 'bin t = { free: 'bin bin list; } type 'tag box = { tag : 'tag ; width : int ; height : int ; allow_rotation : bool } let box ?(allow_rotation=false) tag width height = { allow_rotation; width; height; tag } type ('bin, 'tag) rect = { x : int ; y : int ; w : int ; h : int ; rotated: bool ; bin: 'bin ; box: 'tag box } type heuristic = [ `Short_side_fit | `Long_side_fit | `Area_fit | `Bottom_left ] let empty = { free = [] } let add_bin bin_tag bin_w bin_h t = let rec bin = { bin_x = 0 ; bin_y = 0 ; bin_w; bin_h; bin_tag; bin_root = bin } in { free = bin :: t.free } type score = { hi : int ; lo : int } let score_heuristic = function | `Short_side_fit -> fun rect w h -> let dw = rect.bin_w - w and dh = rect.bin_h - h in { hi = min dw dh; lo = max dw dh } | `Long_side_fit -> fun rect w h -> let dw = rect.bin_w - w and dh = rect.bin_h - h in { hi = max dw dh; lo = min dw dh } | `Bottom_left -> fun rect _w h -> { hi = rect.bin_y + h; lo = rect.bin_x } | `Area_fit -> fun rect w h -> let area_fit = rect.bin_w * rect.bin_h - w * h in let dw = rect.bin_w - w and dh = rect.bin_h - h in { hi = area_fit; lo = min dw dh } let null_score = { hi = max_int; lo = max_int } let null_acc tag bin = (tag, bin, { hi = max_int; lo = max_int }) let score_is_better a b = a.hi < b.hi || (a.hi = b.hi && a.lo < b.lo) let select_best score_fun tag w h (_, _, score0 as acc) rect = if rect.bin_w >= w && rect.bin_h >= h then let score = score_fun rect w h in if score_is_better score score0 then (tag, rect, score) else acc else acc let split_free_node bin used free = let aux rects free = if free.bin_root != bin.bin_root || (used.x >= free.bin_x + free.bin_w || used.x + used.w <= free.bin_x || used.y >= free.bin_y + free.bin_h || used.y + used.h <= free.bin_y) then free :: rects else let rects = ref rects in if (used.x < free.bin_x + free.bin_w && used.x + used.w > free.bin_x) then ( if (used.y > free.bin_y && used.y < free.bin_y + free.bin_h) then (* New node at the top side of the used node. *) rects := { free with bin_h = used.y - free.bin_y } :: !rects; if (used.y + used.h < free.bin_y + free.bin_h) then (* New node at the bottom side of the used node. *) rects := { free with bin_y = used.y + used.h; bin_h = (free.bin_y + free.bin_h) - (used.y + used.h) } :: !rects ); if (used.y < free.bin_y + free.bin_h && used.y + used.h > free.bin_y) then ( if (used.x > free.bin_x && used.x < free.bin_x + free.bin_w) then (* New node at the left side of the used node. *) rects := {free with bin_w = used.x - free.bin_x} :: !rects; if (used.x + used.w < free.bin_x + free.bin_w) then (* New node at the right side of the used node. *) rects := {free with bin_x = used.x + used.w; bin_w = (free.bin_x + free.bin_w) - (used.x + used.w) } :: !rects ); !rects in List.fold_left aux [] free module Pop_array = struct type 'a t = { mutable len: int; arr: 'a array; } let of_list l = let arr = Array.of_list l in { len = Array.length arr; arr } let length t = t.len let get t n = if n < 0 || n >= t.len then invalid_arg "Pop_array.get: index out of bounds"; t.arr.(n) let pop t n = if n < 0 || n >= t.len then invalid_arg "Pop_array.pop: index out of bounds"; let x = t.arr.(n) in let len = t.len - 1 in t.arr.(n) <- t.arr.(len); t.len <- len; x let filter t f = let i = ref 0 in while !i < t.len do while !i < t.len && f !i t.arr.(!i) do ignore (pop t !i); done; incr i done let fold t f acc = let len = t.len in let acc = ref acc in for i = 0 to len - 1 do acc := f i t.arr.(i) !acc; if t.len <> len then invalid_arg "Pop_array.fold: functional argument is mutating the array" done; !acc let maximums pred = function | ([] | [_]) as l -> l | [a;b] as l -> if pred a b then [b] else if pred b a then [a] else l | l -> let a = of_list l in let result = ref [] in while length a > 0 do let item' = ref (get a 0) in filter a (fun _ item -> if pred item !item' then true else if pred !item' item then (item' := item; true) else false); result := !item' :: !result done; !result end let is_contained_in a b = a.bin_root == b.bin_root && a.bin_x >= b.bin_x && a.bin_y >= b.bin_y && a.bin_x+a.bin_w <= b.bin_x+b.bin_w && a.bin_y+a.bin_h <= b.bin_y+b.bin_h (*let rec prune_free_list_reference = function | [] -> [] | r :: rects -> let rec aux r = function | r' :: rects when is_contained_in r r' -> aux r' rects | r' :: rects when is_contained_in r' r -> aux r rects | r' :: rects -> r' :: aux r rects | [] -> [r] in aux r (prune_free_list_reference rects)*) let prune_free_list l = Pop_array.maximums is_contained_in l let used_rect bin box rotated = let w, h = if rotated then box.height, box.width else box.width, box.height in { x = bin.bin_x; y = bin.bin_y; w; h; rotated; bin = bin.bin_tag; box } let update_free bin used {free} = { free = prune_free_list (split_free_node bin used free) } let insert t ?(heuristic=`Short_side_fit) ({ width = w; height = h; _ } as box) = match t.free with | [] -> t, None | default_bin :: _ -> let (rotated, bin, score) = let score_fun = score_heuristic heuristic in let acc = null_acc false default_bin in let acc = List.fold_left (select_best score_fun false w h) acc t.free in if box.allow_rotation && w <> h then List.fold_left (select_best score_fun true h w) acc t.free else acc in if score = null_score then (t, None) else let rect = used_rect bin box rotated in (update_free bin rect t, Some rect) let insert_global t ?(heuristic=`Short_side_fit) boxes = match t.free with | [] -> t, List.map (fun _ -> None) boxes | default_bin :: _ -> let boxes = boxes |> List.mapi (fun i r -> i,r) |> Pop_array.of_list in let result = Array.make (Pop_array.length boxes) None in let score_fun = score_heuristic heuristic in let t = ref t in let select_candidate i (_,{ allow_rotation; width=w; height=h; _ }) acc = let free = (!t).free in let acc = List.fold_left (select_best score_fun (i,false) w h) acc free in if allow_rotation && w <> h then List.fold_left (select_best score_fun (i,true) h w) acc free else acc in begin try while Pop_array.length boxes > 0 do let (pidx, rotated), bin, _score = Pop_array.fold boxes select_candidate (null_acc (-1, false) default_bin) in if pidx = -1 then raise Exit; let (idx, box) = Pop_array.pop boxes pidx in let rect = used_rect bin box rotated in t := update_free bin rect !t; result.(idx) <- Some rect; done; with Exit -> () end; !t, Array.to_list result let insert_batch t ?(heuristic=`Bottom_left) boxes = let cmp (_, b1) (_, b2) = match compare (min b1.width b1.height) (min b2.width b2.height) with | 0 -> compare (max b1.width b1.height) (max b2.width b2.height) | n -> n in let boxes = boxes |> List.mapi (fun i r -> i,r) |> List.sort cmp in let results = Array.make (List.length boxes) None in let t = List.fold_left (fun t (idx, box) -> let t, rect = insert t ~heuristic box in results.(idx) <- rect; t ) t boxes in (t, Array.to_list results)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>