1 /*
2 DSFML - The Simple and Fast Multimedia Library for D
3 
4 Copyright (c) 2013 - 2015 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5 
6 This software is provided 'as-is', without any express or implied warranty.
7 In no event will the authors be held liable for any damages arising from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose, including commercial applications,
10 and to alter it and redistribute it freely, subject to the following restrictions:
11 
12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
14 
15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
16 
17 3. This notice may not be removed or altered from any source distribution
18 */
19 
20 module dsfml.graphics.rect;
21 
22 import std.traits;
23 
24 import dsfml.system.vector2;
25 
26 /++
27  + Utility class for manipulating 2D axis aligned rectangles.
28  + 
29  + A rectangle is defined by its top-left corner and its size.
30  + 
31  + It is a very simple class defined for convenience, so its member variables (left, top, width and height) are public and can be accessed directly, just like the vector classes (Vector2 and Vector3).
32  + 
33  + To keep things simple, Rect doesn't define functions to emulate the properties that are not directly members (such as right, bottom, center, etc.), it rather only provides intersection functions.
34  + 
35  + Rect uses the usual rules for its boundaries:
36  + - The let and top edges are included in the rectangle's area
37  + - The right (left + width) and bottom (top + height) edges are excluded from the rectangle's area
38  + 
39  + This means that IntRect(0, 0, 1, 1) and IntRect(1, 1, 1, 1) don't intersect.
40  + 
41  + Rect is a template and may be used with any numeric type, but for simplicity the instanciations used by SFML are typedefed:
42  + - Rect!(int) is IntRect
43  + - Rect!(float) is FloatRect
44  + 
45  + So that you don't have to care about the template syntax.
46  + 
47  + Authors: Laurent Gomila, Jeremy DeHaan
48  + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Rect.php#details
49  +/
50 struct Rect(T)
51 	if(isNumeric!(T))
52 {
53 	/// Left coordinate of the rectangle.
54 	T left = 0;
55 	/// Top coordinate of the rectangle.
56 	T top = 0;
57 	/// Width of the rectangle.
58 	T width= 0;
59 	/// HEight of the rectangle.
60 	T height = 0;
61 	
62 
63 	this(T rectLeft, T rectTop, T rectWidth, T rectHeight)
64 	{
65 		left = rectLeft;
66 		top = rectTop;
67 		width = rectWidth;
68 		height = rectHeight;
69 	}
70 	
71 	this(Vector2!(T) position, Vector2!(T) size)
72 	{
73 		left = position.x;
74 		top = position.y;
75 		width = size.x;
76 		height = size.y;
77 	}
78 
79 	/**
80 	 * Check if a point is inside the rectangle's area.
81 	 * 
82 	 * Params:
83 	 * 		x	= X coordinate of the point to test
84 	 * 		y	= Y coordinate of the point to test
85 	 * 
86 	 * Returns: True if the point is inside, false otherwise.
87 	 */
88 	bool contains(E)(E X, E Y) const
89 		if(isNumeric!(E))
90 	{
91 		if(left <= X && X<= (left + width))
92 		{
93 			if(top <= Y && Y <= (top + height))
94 			{
95 				return true;
96 			}
97 			else
98 			{
99 				return false;
100 			}
101 		}
102 		else
103 		{
104 			return false;
105 		}
106 	}
107 
108 	/**
109 	 * Check if a point is inside the rectangle's area.
110 	 * 
111 	 * Params:
112 	 * 		point	= Point to test
113 	 * 
114 	 * Returns: True if the point is inside, false otherwise.
115 	 */
116 	bool contains(E)(Vector2!(E) point) const
117 		if(isNumeric!(E))
118 	{
119 		if(left <= point.x && point.x<= (left + width))
120 		{
121 			if(top <= point.y && point.y <= (top + height))
122 			{
123 				return true;
124 			}
125 			else
126 			{
127 				return false;
128 			}
129 		}
130 		else
131 		{
132 			return false;
133 		}
134 	}
135 
136 	/**
137 	 * Check the intersection between two rectangles.
138 	 * 
139 	 * Params:
140 	 * 		rectangle	= Rectangle to test
141 	 * 
142 	 * Returns: True if rectangles overlap, false otherwise.
143 	 */
144 	bool intersects(E)(Rect!(E) rectangle) const
145 	if(isNumeric!(E))
146 	{
147 		Rect!(T) rect;
148 		
149 		return intersects(rectangle, rect);
150 	}
151 
152 	/**
153 	 * Check the intersection between two rectangles.
154 	 * 
155 	 * This overload returns the overlapped rectangle in the intersection parameter.
156 	 * 
157 	 * Params:
158 	 * 		rectangle		= Rectangle to test
159 	 * 		intersection	= Rectangle to be filled with the intersection
160 	 * 
161 	 * Returns: True if rectangles overlap, false otherwise.
162 	 */
163 	bool intersects(E,O)(Rect!(E) rectangle, out Rect!(O) intersection) const
164 		if(isNumeric!(E) && isNumeric!(O))
165 	{
166 		O interLeft = intersection.max(left, rectangle.left);
167 		O interTop = intersection.max(top, rectangle.top);
168 		O interRight = intersection.min(left + width, rectangle.left + rectangle.width);
169 		O interBottom = intersection.min(top + height, rectangle.top + rectangle.height);
170 		
171 		if ((interLeft < interRight) && (interTop < interBottom))
172 		{
173 			intersection = Rect!(O)(interLeft, interTop, interRight - interLeft, interBottom - interTop);
174 			return true;
175 		}
176 		else
177 		{
178 			intersection = Rect!(O)(0, 0, 0, 0);
179 			return false;
180 		}
181 	}
182 
183 	bool opEquals(E)(const Rect!(E) otherRect) const
184 		if(isNumeric!(E))
185 	{
186 		return ((left == otherRect.left) && (top == otherRect.top) && (width == otherRect.width) && (height == otherRect.height) );
187 	}
188 
189 	string toString()
190 	{
191 		import std.conv;
192 		return "Left: " ~ text(left) ~ " Top: " ~ text(top) ~ " Width: " ~ text(width) ~ " Height: " ~ text(height);
193 	}
194 	
195 	private T max(T a, T b)
196 	{
197 		if(a>b)
198 		{
199 			return a;
200 		}
201 		else
202 		{
203 			return b;
204 		}
205 	}
206 	
207 	private T min(T a, T b)
208 	{
209 		if(a<b)
210 		{
211 			return a;
212 		}
213 		else
214 		{
215 			return b;
216 		}
217 	}
218 
219 }
220 
221 unittest
222 {
223 	version(DSFML_Unittest_Graphics)
224 	{
225 		import std.stdio;
226 
227 		writeln("Unit test for Rect");
228 
229 		auto rect1 = IntRect(0,0,100,100);
230 		auto rect2 = IntRect(10,10,100,100);
231 		auto rect3 = IntRect(10,10,10,10);
232 		auto point = Vector2f(-20,-20);
233 
234 
235 
236 		assert(rect1.intersects(rect2));
237 
238 		FloatRect interRect;
239 
240 		rect1.intersects(rect2, interRect);
241 
242 		assert(interRect == IntRect(10,10, 90, 90));
243 
244 		assert(rect1.contains(10,10));
245 
246 		assert(!rect1.contains(point));
247 
248 		writeln();
249 	}
250 }
251 
252 alias Rect!(int) IntRect;
253 alias Rect!(float) FloatRect;