1 module anchovy.utils.rect;
2 
3 import std.conv;
4 import dlib.math.utils: clampValue = clamp;
5 import dlib.math.vector;
6 import anchovy.utils.rectoffset;
7 
8 struct Rect
9 {
10 	union
11 	{
12 		struct
13 		{
14 			int x, y, width, height;
15 		}
16 		int[4] arrayof;
17 	}
18 
19 	this(int[4] array) @safe
20 	{
21 		arrayof = array;
22 	}
23 
24 	this(int a, int b, int c, int d) @safe
25 	{
26 		x = a;
27 		y = b;
28 		width = c;
29 		height = d;
30 	}
31 
32 	this(ivec2 position, ivec2 size)
33 	{
34 		x = position.x;
35 		y = position.y;
36 		width = size.x;
37 		height = size.y;
38 	}
39 
40 	void move(ivec2 delta)
41 	{
42 		x += delta.x;
43 		y += delta.y;
44 	}
45 
46 	bool contains(ivec2 point) nothrow @trusted
47 	{
48 		return contains(point.x, point.y);
49 	}
50 
51 	bool contains(int pointX, int pointY) nothrow @trusted
52 	{
53 		if(pointX < x) return false;
54 		if(pointY < y) return false;
55 		if(pointX > x + width) return false;
56 		if(pointY > y + height) return false;
57 		return true;
58 	}
59 
60 	void cropOffset(RectOffset offset) nothrow
61 	{
62 		x += offset.left;
63 		y += offset.top;
64 		width -= offset.horizontal;
65 		height -= offset.vertical;
66 	}
67 
68 	Rect croppedByOffset(RectOffset offset) nothrow
69 	{
70 		Rect result;
71 		result.x = x + offset.left;
72 		result.y = y + offset.top;
73 		result.width = width - offset.horizontal;
74 		result.height = height - offset.vertical;
75 		return result;
76 	}
77 
78 	/// Increases size by deltaSize.
79 	void grow(ivec2 deltaSize)
80 	{
81 		width += deltaSize.x;
82 		height += deltaSize.y;
83 
84 		if (width < 0) width = 0;
85 		if (height < 0) height = 0;
86 	}
87 
88 	Rect growed(ivec2 deltaSize)
89 	{
90 		Rect newRect;
91 		newRect.width = width + deltaSize.x;
92 		newRect.height = height + deltaSize.y;
93 
94 		if (newRect.width < 0) newRect.width = 0;
95 		if (newRect.height < 0) newRect.height = 0;
96 
97 		return newRect;
98 	}
99 
100 	Rect relativeToParent(Rect parent) @safe
101 	{
102 		return Rect(x + parent.x, y + parent.y, width, height);
103 	}
104 
105 	/// Returns true if size was changed
106 	void clampSize(uvec2 minSize, uvec2 maxSize) nothrow
107 	{
108 		if (maxSize.x == 0)
109 		{
110 			width = clampValue!uint(width, minSize.x, width);
111 		}
112 		else
113 		{
114 			width = clampValue!uint(width, minSize.x, maxSize.x);
115 		}
116 
117 		if (maxSize.y == 0)
118 		{
119 			height = clampValue!uint(height, minSize.y, height);
120 		}
121 		else
122 		{
123 			height = clampValue!uint(height, minSize.y, maxSize.y);
124 		}
125 	}
126 
127 	ivec2 position() @property
128 	{
129 		return ivec2(x, y);
130 	}
131 
132 	ivec2 size() @property
133 	{
134 		return ivec2(width, height);
135 	}
136 
137 	string toString()
138 	{
139 		return to!string(arrayof);
140 	}
141 }
142 
143 unittest
144 {
145 	Rect rect = Rect(-5, -5, 10, 10);
146 
147 	assert(!rect.contains(-10, 0));
148 	assert(!rect.contains(6, 0));
149 	assert(!rect.contains(0, -6));
150 	assert(!rect.contains(9, 6));
151 	assert(rect.contains(0, 0));
152 }