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