Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file maxrects.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272type'binbin={bin_x:int;bin_y:int;bin_w:int;bin_h:int;bin_root:'binbin;bin_tag:'bin;}type'bint={free:'binbinlist;}type'tagbox={tag:'tag;width:int;height:int;allow_rotation:bool}letbox?(allow_rotation=false)tagwidthheight={allow_rotation;width;height;tag}type('bin,'tag)rect={x:int;y:int;w:int;h:int;rotated:bool;bin:'bin;box:'tagbox}typeheuristic=[`Short_side_fit|`Long_side_fit|`Area_fit|`Bottom_left]letempty={free=[]}letadd_binbin_tagbin_wbin_ht=letrecbin={bin_x=0;bin_y=0;bin_w;bin_h;bin_tag;bin_root=bin}in{free=bin::t.free}typescore={hi:int;lo:int}letscore_heuristic=function|`Short_side_fit->funrectwh->letdw=rect.bin_w-wanddh=rect.bin_h-hin{hi=mindwdh;lo=maxdwdh}|`Long_side_fit->funrectwh->letdw=rect.bin_w-wanddh=rect.bin_h-hin{hi=maxdwdh;lo=mindwdh}|`Bottom_left->funrect_wh->{hi=rect.bin_y+h;lo=rect.bin_x}|`Area_fit->funrectwh->letarea_fit=rect.bin_w*rect.bin_h-w*hinletdw=rect.bin_w-wanddh=rect.bin_h-hin{hi=area_fit;lo=mindwdh}letnull_score={hi=max_int;lo=max_int}letnull_acctagbin=(tag,bin,{hi=max_int;lo=max_int})letscore_is_betterab=a.hi<b.hi||(a.hi=b.hi&&a.lo<b.lo)letselect_bestscore_funtagwh(_,_,score0asacc)rect=ifrect.bin_w>=w&&rect.bin_h>=hthenletscore=score_funrectwhinifscore_is_betterscorescore0then(tag,rect,score)elseaccelseaccletsplit_free_nodebinusedfree=letauxrectsfree=iffree.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)thenfree::rectselseletrects=refrectsinif(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:={freewithbin_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:={freewithbin_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:={freewithbin_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:={freewithbin_x=used.x+used.w;bin_w=(free.bin_x+free.bin_w)-(used.x+used.w)}::!rects);!rectsinList.fold_leftaux[]freemodulePop_array=structtype'at={mutablelen:int;arr:'aarray;}letof_listl=letarr=Array.of_listlin{len=Array.lengtharr;arr}letlengtht=t.lenletgettn=ifn<0||n>=t.lentheninvalid_arg"Pop_array.get: index out of bounds";t.arr.(n)letpoptn=ifn<0||n>=t.lentheninvalid_arg"Pop_array.pop: index out of bounds";letx=t.arr.(n)inletlen=t.len-1int.arr.(n)<-t.arr.(len);t.len<-len;xletfiltertf=leti=ref0inwhile!i<t.lendowhile!i<t.len&&f!it.arr.(!i)doignore(popt!i);done;incridoneletfoldtfacc=letlen=t.leninletacc=refaccinfori=0tolen-1doacc:=fit.arr.(i)!acc;ift.len<>lentheninvalid_arg"Pop_array.fold: functional argument is mutating the array"done;!accletmaximumspred=function|([]|[_])asl->l|[a;b]asl->ifpredabthen[b]elseifpredbathen[a]elsel|l->leta=of_listlinletresult=ref[]inwhilelengtha>0doletitem'=ref(geta0)infiltera(fun_item->ifpreditem!item'thentrueelseifpred!item'itemthen(item':=item;true)elsefalse);result:=!item'::!resultdone;!resultendletis_contained_inab=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)*)letprune_free_listl=Pop_array.maximumsis_contained_inlletused_rectbinboxrotated=letw,h=ifrotatedthenbox.height,box.widthelsebox.width,box.heightin{x=bin.bin_x;y=bin.bin_y;w;h;rotated;bin=bin.bin_tag;box}letupdate_freebinused{free}={free=prune_free_list(split_free_nodebinusedfree)}letinsertt?(heuristic=`Short_side_fit)({width=w;height=h;_}asbox)=matcht.freewith|[]->t,None|default_bin::_->let(rotated,bin,score)=letscore_fun=score_heuristicheuristicinletacc=null_accfalsedefault_bininletacc=List.fold_left(select_bestscore_funfalsewh)acct.freeinifbox.allow_rotation&&w<>hthenList.fold_left(select_bestscore_funtruehw)acct.freeelseaccinifscore=null_scorethen(t,None)elseletrect=used_rectbinboxrotatedin(update_freebinrectt,Somerect)letinsert_globalt?(heuristic=`Short_side_fit)boxes=matcht.freewith|[]->t,List.map(fun_->None)boxes|default_bin::_->letboxes=boxes|>List.mapi(funir->i,r)|>Pop_array.of_listinletresult=Array.make(Pop_array.lengthboxes)Noneinletscore_fun=score_heuristicheuristicinlett=reftinletselect_candidatei(_,{allow_rotation;width=w;height=h;_})acc=letfree=(!t).freeinletacc=List.fold_left(select_bestscore_fun(i,false)wh)accfreeinifallow_rotation&&w<>hthenList.fold_left(select_bestscore_fun(i,true)hw)accfreeelseaccinbegintrywhilePop_array.lengthboxes>0dolet(pidx,rotated),bin,_score=Pop_array.foldboxesselect_candidate(null_acc(-1,false)default_bin)inifpidx=-1thenraiseExit;let(idx,box)=Pop_array.popboxespidxinletrect=used_rectbinboxrotatedint:=update_freebinrect!t;result.(idx)<-Somerect;done;withExit->()end;!t,Array.to_listresultletinsert_batcht?(heuristic=`Bottom_left)boxes=letcmp(_,b1)(_,b2)=matchcompare(minb1.widthb1.height)(minb2.widthb2.height)with|0->compare(maxb1.widthb1.height)(maxb2.widthb2.height)|n->ninletboxes=boxes|>List.mapi(funir->i,r)|>List.sortcmpinletresults=Array.make(List.lengthboxes)Noneinlett=List.fold_left(funt(idx,box)->lett,rect=insertt~heuristicboxinresults.(idx)<-rect;t)tboxesin(t,Array.to_listresults)