1 /* 2 * DSFML - The Simple and Fast Multimedia Library for D 3 * 4 * Copyright (c) 2013 - 2017 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 8 * use of this software. 9 * 10 * Permission is granted to anyone to use this software for any purpose, 11 * including commercial applications, and to alter it and redistribute it 12 * freely, subject to the following restrictions: 13 * 14 * 1. The origin of this software must not be misrepresented; you must not claim 15 * that you wrote the original software. If you use this software in a product, 16 * an acknowledgment in the product documentation would be appreciated but is 17 * not required. 18 * 19 * 2. Altered source versions must be plainly marked as such, and must not be 20 * misrepresented as being the original software. 21 * 22 * 3. This notice may not be removed or altered from any source distribution 23 */ 24 25 /** 26 * A rectangle is defined by its top-left corner and its size. It is a very 27 * simple class defined for convenience, so its member variables (`left`, `top`, 28 * `width`, and `height`) are public and can be accessed directly, just like the 29 * vector classes ($(VECTOR2_LINK) and $(VECTOR3_LINK)). 30 * 31 * To keep things simple, $(U Rect) doesn't define functions to emulate the 32 * properties that are not directly members (such as right, bottom, center, 33 * etc.), it rather only provides intersection functions. 34 * 35 * Rect uses the usual rules for its boundaries: 36 * $(UL 37 * $(LI The let and top edges are included in the rectangle's area) 38 * $(LI The right (left + width) and bottom (top + height) edges are excluded 39 * from the rectangle's area)) 40 * 41 * $(PARA This means that `IntRect(0, 0, 1, 1)` and `IntRect(1, 1, 1, 1)` don't 42 * intersect. 43 * 44 * $(U Rect) is a template and may be used with any numeric type, but for 45 * simplicity the instanciations used by SFML are aliased:) 46 * $(UL 47 * $(LI Rect!(int) is IntRect) 48 * $(LI Rect!(float) is FloatRect)) 49 * 50 * $(PARA This is so you don't have to care about the template syntax.) 51 * 52 * Example: 53 * --- 54 * // Define a rectangle, located at (0, 0) with a size of 20x5 55 * auto r1 = IntRect(0, 0, 20, 5); 56 * 57 * // Define another rectangle, located at (4, 2) with a size of 18x10 58 * auto position = Vector2i(4, 2); 59 * auto size = Vector2i(18, 10); 60 * auto r2 = IntRect(position, size); 61 * 62 * // Test intersections with the point (3, 1) 63 * bool b1 = r1.contains(3, 1); // true 64 * bool b2 = r2.contains(3, 1); // false 65 * 66 * // Test the intersection between r1 and r2 67 * IntRect result; 68 * bool b3 = r1.intersects(r2, result); // true 69 * // result == IntRect(4, 2, 16, 3) 70 * --- 71 */ 72 module dsfml.graphics.rect; 73 74 import std.traits; 75 76 import dsfml.system.vector2; 77 78 /** 79 * Utility class for manipulating 2D axis aligned rectangles. 80 */ 81 struct Rect(T) 82 if(isNumeric!(T)) 83 { 84 /// Left coordinate of the rectangle. 85 T left = 0; 86 /// Top coordinate of the rectangle. 87 T top = 0; 88 /// Width of the rectangle. 89 T width= 0; 90 /// Height of the rectangle. 91 T height = 0; 92 93 /** 94 * Construct the rectangle from its coordinates 95 * 96 * Be careful, the last two parameters are the width 97 * and height, not the right and bottom coordinates! 98 * 99 * Params: 100 * rectLeft = Left coordinate of the rectangle 101 * rectTop = Top coordinate of the rectangle 102 * rectWidth = Width of the rectangle 103 * rectHeight = Height of the rectangle 104 */ 105 this(T rectLeft, T rectTop, T rectWidth, T rectHeight) 106 { 107 left = rectLeft; 108 top = rectTop; 109 width = rectWidth; 110 height = rectHeight; 111 } 112 113 /** 114 * Construct the rectangle from position and size 115 * 116 * Be careful, the last parameter is the size, 117 * not the bottom-right corner! 118 * 119 * Params: 120 * position = Position of the top-left corner of the rectangle 121 * size = Size of the rectangle 122 */ 123 this(Vector2!(T) position, Vector2!(T) size) 124 { 125 left = position.x; 126 top = position.y; 127 width = size.x; 128 height = size.y; 129 } 130 131 /** 132 * Check if a point is inside the rectangle's area. 133 * 134 * Params: 135 * x = X coordinate of the point to test 136 * y = Y coordinate of the point to test 137 * 138 * Returns: true if the point is inside, false otherwise. 139 */ 140 bool contains(E)(E X, E Y) const 141 if(isNumeric!(E)) 142 { 143 if(left <= X && X<= (left + width)) 144 { 145 if(top <= Y && Y <= (top + height)) 146 { 147 return true; 148 } 149 else 150 { 151 return false; 152 } 153 } 154 else 155 { 156 return false; 157 } 158 } 159 160 /** 161 * Check if a point is inside the rectangle's area. 162 * 163 * Params: 164 * point = Point to test 165 * 166 * Returns: true if the point is inside, false otherwise. 167 */ 168 bool contains(E)(Vector2!(E) point) const 169 if(isNumeric!(E)) 170 { 171 if(left <= point.x && point.x<= (left + width)) 172 { 173 if(top <= point.y && point.y <= (top + height)) 174 { 175 return true; 176 } 177 else 178 { 179 return false; 180 } 181 } 182 else 183 { 184 return false; 185 } 186 } 187 188 /** 189 * Check the intersection between two rectangles. 190 * 191 * Params: 192 * rectangle = Rectangle to test 193 * 194 * Returns: true if rectangles overlap, false otherwise. 195 */ 196 bool intersects(E)(Rect!(E) rectangle) const 197 if(isNumeric!(E)) 198 { 199 Rect!(T) rect; 200 201 return intersects(rectangle, rect); 202 } 203 204 /** 205 * Check the intersection between two rectangles. 206 * 207 * This overload returns the overlapped rectangle in the intersection 208 * parameter. 209 * 210 * Params: 211 * rectangle = Rectangle to test 212 * intersection = Rectangle to be filled with the intersection 213 * 214 * Returns: true if rectangles overlap, false otherwise. 215 */ 216 bool intersects(E,O)(Rect!(E) rectangle, out Rect!(O) intersection) const 217 if(isNumeric!(E) && isNumeric!(O)) 218 { 219 O interLeft = intersection.max(left, rectangle.left); 220 O interTop = intersection.max(top, rectangle.top); 221 O interRight = intersection.min(left + width, rectangle.left + rectangle.width); 222 O interBottom = intersection.min(top + height, rectangle.top + rectangle.height); 223 224 if ((interLeft < interRight) && (interTop < interBottom)) 225 { 226 intersection = Rect!(O)(interLeft, interTop, interRight - interLeft, interBottom - interTop); 227 return true; 228 } 229 else 230 { 231 intersection = Rect!(O)(0, 0, 0, 0); 232 return false; 233 } 234 } 235 236 /// Compare two rectangles for equality. 237 bool opEquals(E)(const Rect!(E) otherRect) const 238 if(isNumeric!(E)) 239 { 240 return ((left == otherRect.left) && (top == otherRect.top) && (width == otherRect.width) && (height == otherRect.height) ); 241 } 242 243 /// Output the string representation of the Rect. 244 string toString() 245 { 246 import std.conv; 247 return "Left: " ~ text(left) ~ " Top: " ~ text(top) ~ " Width: " ~ text(width) ~ " Height: " ~ text(height); 248 } 249 250 private T max(T a, T b) 251 { 252 return a>b?a:b; 253 } 254 255 private T min(T a, T b) 256 { 257 return a<b?a:b; 258 } 259 } 260 261 unittest 262 { 263 version(DSFML_Unittest_Graphics) 264 { 265 import std.stdio; 266 267 writeln("Unit test for Rect"); 268 269 auto rect1 = IntRect(0,0,100,100); 270 auto rect2 = IntRect(10,10,100,100); 271 auto rect3 = IntRect(10,10,10,10); 272 auto point = Vector2f(-20,-20); 273 274 assert(rect1.intersects(rect2)); 275 276 FloatRect interRect; 277 278 rect1.intersects(rect2, interRect); 279 280 assert(interRect == IntRect(10,10, 90, 90)); 281 282 assert(rect1.contains(10,10)); 283 284 assert(!rect1.contains(point)); 285 286 writeln(); 287 } 288 } 289 290 /// Definition of a Rect using integers. 291 alias Rect!(int) IntRect; 292 /// Definition of a Rect using floats. 293 alias Rect!(float) FloatRect;