1 module atlas; 2 3 import atlas.pixel_scan, atlas.rows; 4 5 import core.stdc.stdlib: qsort; 6 7 /** 8 A rectangular region with an optional `userData` field of type `U` to associate it with an external resource (e.g. an image). 9 */ 10 struct Rect(U){ 11 uint w = 0, h = 0; 12 static if(!is(U == void)){ 13 U userData; 14 } 15 16 //the following data should not be entered by the user, but can be read after calling `pack`. 17 uint x = 0, y = 0; 18 bool packed = false; 19 } 20 21 enum isRect(T) = is(T TT: Rect!(U), U); 22 23 ///A method to pack rectangles together. 24 enum Method{ 25 /** 26 A basic, fast, but wasteful algorithm. (~6–10% waste) 27 28 It inserts the tallest unpacked rectangle and moves right until it reaches 29 the right edge, then goes down by the height of the tallest rectangle in that row. 30 */ 31 rows, 32 /** 33 A relatively simple but slow algorithm, that tends to waste very little space. (~2–6% waste) 34 35 It inserts the tallest unpacked rectangle into the topmost, leftmost 36 space large enough to accommodate it. 37 */ 38 pixelScan, 39 } 40 41 /** 42 Packs `rects` into a rectangular region with size `width` by `height`, using `method`. 43 Accounts for `padding` grid-spaces of padding on all sides of the `rects`. 44 The data in `rects` will be re-ordered. 45 46 Returns: A slice of `rects`, containing only the rectangles that could fit into the packing region. 47 */ 48 R[] pack(R)(scope return ref R[] rects, uint width, uint height, Method method, uint padding=0) nothrow @nogc 49 if(isRect!R){ 50 with(Method) final switch(method){ 51 case rows, pixelScan: 52 qsort(&rects[0], rects.length, rects[0].sizeof, &sortRectHeight!R); 53 break; 54 } 55 return packSorted(rects, width, height, method, padding); 56 } 57 58 extern(C) private int sortRectHeight(R)(const(void)* aPtr, const(void)* bPtr) nothrow @nogc{ 59 auto a = cast(const(R)*)aPtr; 60 auto b = cast(const(R)*)bPtr; 61 return b.h - a.h; 62 } 63 64 /** 65 Same as `pack`, but requires `rects` to be sorted by their height (tallest first) for the following methods: 66 - rows 67 68 Note that even for methods that don't require sorting, the results will still be inferior if the rectangles aren't sorted by height. 69 */ 70 R[] packSorted(R)(scope return ref R[] rects, uint width, uint height, Method method, uint padding=0) nothrow @nogc pure @safe 71 if(isRect!R){ 72 if(padding){ 73 foreach(ref rect; rects){ 74 rect.w += padding * 2; 75 rect.h += padding * 2; 76 } 77 } 78 auto ret = { 79 with(Method) final switch(method){ 80 case rows: return packRows(rects, width, height); 81 case pixelScan: return packPixelScan(rects, width, height); 82 } 83 }(); 84 if(padding){ 85 foreach(ref rect; rects){ 86 rect.x += padding; 87 rect.y += padding; 88 rect.w -= padding * 2; 89 rect.h -= padding * 2; 90 } 91 } 92 return ret; 93 }