1 /* 2 ******************************************************************************************* 3 * Dgame (a D game framework) - Copyright (c) Randy Schütt 4 * 5 * This software is provided 'as-is', without any express or implied warranty. 6 * In no event will the authors be held liable for any damages arising from 7 * the use of this software. 8 * 9 * Permission is granted to anyone to use this software for any purpose, 10 * including commercial applications, and to alter it and redistribute it 11 * freely, subject to the following restrictions: 12 * 13 * 1. The origin of this software must not be misrepresented; you must not claim 14 * that you wrote the original software. If you use this software in a product, 15 * an acknowledgment in the product documentation would be appreciated but is 16 * not required. 17 * 18 * 2. Altered source versions must be plainly marked as such, and must not be 19 * misrepresented as being the original software. 20 * 21 * 3. This notice may not be removed or altered from any source distribution. 22 ******************************************************************************************* 23 */ 24 module Dgame.Math.Rect; 25 26 private { 27 debug import std.stdio; 28 import std.traits : isNumeric; 29 30 import derelict.sdl2.sdl; 31 32 import Dgame.Math.Vector2; 33 import Dgame.Internal.Unique; 34 } 35 36 SDL_Rect* transfer(T)(const Rect!(T)* rect, SDL_Rect* to) pure nothrow 37 in { 38 assert(to !is null); 39 } body { 40 if (rect is null) 41 return null; 42 43 rect.transferTo(to); 44 45 return to; 46 } 47 48 /** 49 * Rect defines a rectangle structure that contains the left upper corner and the width/height. 50 * 51 * Author: rschuett 52 */ 53 struct Rect(T) if (isNumeric!T) { 54 /** 55 * The x 56 */ 57 T x = 0; 58 /** 59 * and y coordinates 60 */ 61 T y = 0; 62 /** 63 * The width 64 */ 65 T width = 0; 66 /** 67 * and the height 68 */ 69 T height = 0; 70 71 /** 72 * CTor 73 */ 74 this(T x, T y, T width, T height) pure nothrow { 75 this.x = x; 76 this.y = y; 77 78 this.width = width; 79 this.height = height; 80 } 81 82 /** 83 * CTor 84 */ 85 this(ref const Vector2!(T) vec, T width, T height) pure nothrow { 86 this(vec.x, vec.y, width, height); 87 } 88 89 /** 90 * CTor 91 */ 92 this(U)(ref const Rect!U rect) pure nothrow { 93 static if (is(U : T)) { 94 this(rect.x, rect.y, rect.width, rect.height); 95 } else { 96 this(cast(T) rect.x, cast(T) rect.y, 97 cast(T) rect.width, cast(T) rect.height); 98 } 99 } 100 101 this(ref const SDL_Rect rect) { 102 this(cast(T) rect.x, cast(T) rect.y, 103 cast(T) rect.w, cast(T) rect.h); 104 } 105 106 // debug(Dgame) 107 // this(this) { 108 // writeln("Postblit"); 109 // } 110 // 111 // debug(Dgame) 112 // ~this() { 113 // debug writeln("DTor Rect"); 114 // } 115 116 /** 117 * Transfer the internal data to the SDL_Rect. 118 */ 119 void transferTo(SDL_Rect* rect) const pure nothrow 120 in { 121 assert(rect !is null, "Cannot transfer anything to null."); 122 } body { 123 rect.x = cast(int) this.x; 124 rect.y = cast(int) this.y; 125 rect.w = cast(int) this.width; 126 rect.h = cast(int) this.height; 127 } 128 129 /** 130 * Supported operations: +=, -=, *=, /=, %= 131 */ 132 Rect opBinary(string op)(ref const Rect rect) const pure nothrow { 133 switch (op) { 134 case "+": 135 return Rect(this.x + rect.x, 136 this.y + rect.y, 137 this.width + rect.width, 138 this.height + rect.height); 139 case "-": 140 return Rect(this.x - rect.x, 141 this.y - rect.y, 142 this.width - rect.width, 143 this.height - rect.height); 144 case "*": 145 return Rect(this.x * rect.x, 146 this.y * rect.y, 147 this.width * rect.width, 148 this.height * rect.height); 149 case "/": 150 return Rect(this.x / rect.x, 151 this.y / rect.y, 152 this.width / rect.width, 153 this.height / rect.height); 154 case "%": 155 return Rect(this.x % rect.x, 156 this.y % rect.y, 157 this.width % rect.width, 158 this.height % rect.height); 159 default: 160 throw new Exception("Unsupported Operation: " ~ op); 161 } 162 } 163 164 /** 165 * Collapse this Rect. Means that the coordinates and the size is set to 0. 166 */ 167 void collapse() pure nothrow { 168 this.width = this.height = 0; 169 this.x = this.y = 0; 170 } 171 172 /** 173 * Checks if this Rect is empty (if it's collapsed) with SDL_RectEmpty. 174 */ 175 bool isEmpty() const { 176 SDL_Rect a = void; 177 this.transferTo(&a); 178 179 return SDL_RectEmpty(&a) == SDL_TRUE; 180 } 181 182 /** 183 * Checks if this Rect is collapsed, which means 184 * that the width and/or the height are <= 0. 185 * This is a pure and nothrow variant of isEmpty. 186 */ 187 bool isCollapsed() const pure nothrow { 188 return this.width <= 0 || this.height <= 0; 189 } 190 191 /** 192 * Checks if all corners are zero. 193 */ 194 bool isZero() const pure nothrow { 195 return this.x == 0 && this.y == 0 && this.width == 0 && this.height == 0; 196 } 197 198 /** 199 * Returns an union of the given and this Rect. 200 */ 201 Rect getUnion(ref const Rect rect) const { 202 SDL_Rect a = void; 203 SDL_Rect b = void; 204 SDL_Rect c = void; 205 206 this.transferTo(&a); 207 rect.transferTo(&b); 208 209 SDL_UnionRect(&a, &b, &c); 210 211 return Rect(c); 212 } 213 214 /** 215 * Checks whether this Rect contains the given coordinates. 216 */ 217 bool opBinaryRight(string op : "in")(ref Vector2!(T) vec) const pure nothrow { 218 return this.contains(vec); 219 } 220 221 /** 222 * Checks whether this Rect contains the given coordinates. 223 */ 224 bool contains(ref const Vector2!(T) vec) const pure nothrow { 225 return this.contains(vec.x, vec.y); 226 } 227 228 /** 229 * Checks whether this Rect contains the given coordinates. 230 */ 231 bool contains(T x, T y) const pure nothrow { 232 return (x >= this.x) && (x < this.x + this.width) 233 && (y >= this.y) && (y < this.y + this.height); 234 } 235 236 /** 237 * opEquals: compares two rectangles on their coordinates and their size (but not explicit type). 238 */ 239 bool opEquals(ref const Rect rect) const { 240 SDL_Rect a = void; 241 SDL_Rect b = void; 242 243 this.transferTo(&a); 244 rect.transferTo(&b); 245 246 return SDL_RectEquals(&a, &b); 247 } 248 249 /** 250 * opCast to another Rect type. 251 */ 252 Rect!U opCast(V : Rect!U, U)() const pure nothrow { 253 return Rect!U(cast(U) this.x, cast(U) this.y, 254 cast(U) this.width, cast(U) this.height); 255 } 256 257 /** 258 * Checks whether this Rect intersects with an other. 259 * If, and the parameter 'overlap' isn't null, 260 * the colliding rectangle is stored there. 261 */ 262 bool intersects(ref const Rect rect, Rect* overlap = null) const { 263 SDL_Rect a = void; 264 SDL_Rect b = void; 265 266 this.transferTo(&a); 267 rect.transferTo(&b); 268 269 if (overlap is null) 270 return SDL_HasIntersection(&a, &b) == SDL_TRUE; 271 272 SDL_Rect c = void; 273 274 const bool intersects = SDL_IntersectRect(&a, &b, &c) == SDL_TRUE; 275 overlap.set(cast(T) c.x, cast(T) c.y, 276 cast(T) c.w, cast(T) c.h); 277 278 return intersects; 279 } 280 281 /** 282 * Use this function to calculate a minimal rectangle enclosing a set of points. 283 */ 284 static Rect enclosePoints(in Vector2!(T)[] points) { 285 unique_ptr!(SDL_Point) sdl_points = allocate_unique!(SDL_Point)(points.length); 286 // SDL_Point[] sdl_points = new SDL_Point[points.length]; 287 288 foreach (i, ref const Vector2!(T) p; points) { 289 sdl_points[i] = SDL_Point(cast(int) p.x, cast(int) p.y); 290 } 291 292 SDL_Rect a = void; 293 SDL_EnclosePoints(&sdl_points[0], cast(uint) points.length, null, &a); 294 295 return Rect(a); 296 } 297 298 /** 299 * Replace current size. 300 */ 301 void setSize(T width, T height) pure nothrow { 302 this.width = width; 303 this.height = height; 304 } 305 306 /** 307 * Returns the current size as Vector2 308 */ 309 Vector2!(T) getSize() const pure nothrow { 310 return Vector2!(T)(this.width, this.height); 311 } 312 313 /** 314 * Increase current size. 315 */ 316 void increase(T width, T height) pure nothrow { 317 this.width += width; 318 this.height += height; 319 } 320 321 /** 322 * Set a new position with coordinates. 323 */ 324 void setPosition(T x, T y) pure nothrow { 325 this.x = x; 326 this.y = y; 327 } 328 329 /** 330 * Set a new position with a vector. 331 */ 332 void setPosition(ref const Vector2!(T) position) pure nothrow { 333 this.setPosition(position.x, position.y); 334 } 335 336 /** 337 * Returns the current position as Vector2 338 */ 339 Vector2!(T) getPosition() const pure nothrow { 340 return Vector2!(T)(this.x, this.y); 341 } 342 343 /** 344 * Move the object. 345 */ 346 void move(ref const Vector2!(T) vec) pure nothrow { 347 this.move(vec.x, vec.y); 348 } 349 350 /** 351 * Move the object. 352 */ 353 void move(T x, T y) pure nothrow { 354 this.x += x; 355 this.y += y; 356 } 357 358 /** 359 * The new coordinates <b>and</b> a new size. 360 */ 361 void set(T x, T y, T w, T h) pure nothrow { 362 this.setPosition(x, y); 363 this.setSize(w, h); 364 } 365 366 /** 367 * Returns the coordinates as static array 368 */ 369 T[4] asArray() const pure nothrow { 370 return [this.x, this.y, this.width, this.height]; 371 } 372 } 373 374 /** 375 * alias for float 376 */ 377 alias FloatRect = Rect!(float); 378 /** 379 * alias for short 380 */ 381 alias ShortRect = Rect!(short);