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 }