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 
28 import derelict.sdl2.sdl;
29 
30 import Dgame.Math.Vector2;
31 
32 package(Dgame):
33 
34 @nogc
35 SDL_Rect* _transfer(ref const Rect rect, ref SDL_Rect to) pure nothrow {
36     to.x = rect.x;
37     to.y = rect.y;
38     to.w = rect.width;
39     to.h = rect.height;
40 
41     return &to;
42 }
43 
44 public:
45 
46 /**
47  * Size contains a width and a height property and 
48  * is used to wraps both, the width and the height, in one element.
49  */
50 struct Size {
51     /**
52      * the width property
53      */
54     uint width;
55     /**
56      * the height property
57      */
58     uint height;
59 }
60 
61 /**
62  * Rect defines a rectangle structure that contains the left upper corner and the width/height.
63  *
64  * Author: Randy Schuett (rswhite4@googlemail.com)
65  */
66 struct Rect {
67     /**
68      * The edges of the Rectangle
69      */
70     enum Edge : ubyte {
71         Top, /// Top edge
72         Bottom, /// Bottom edge
73         Left, /// Left edge
74         Right, /// Right edge
75         
76         TopLeft, /// TopLeft edge
77         TopRight, /// TopRight edge
78         BottomLeft, /// BottomLeft edge
79         BottomRight /// BottomRight edge
80     }
81 
82     /**
83      * The x coordinate
84      */
85     int x = 0;
86     /**
87      * The y coordinates
88      */
89     int y = 0;
90     /**
91      * The width
92      */
93     uint width = 0;
94     /**
95      * The height
96      */
97     uint height = 0;
98     
99     /**
100      * CTor
101      */
102     @nogc
103     this(int x, int y, uint width, uint height) pure nothrow {
104         this.x = x;
105         this.y = y;
106         this.width  = width;
107         this.height = height;
108     }
109 
110     /**
111      * Supported operations: +, -, *, /, %
112      */
113     @nogc
114     Rect opBinary(string op)(const Rect rect) const pure nothrow {
115         switch (op) {
116             case "+":
117             case "-":
118             case "*":
119             case "/":
120             case "%":
121                 mixin("return Rect(this.x " ~ op ~ " rect.x,
122                               this.y " ~ op ~ " rect.y,
123                               this.width " ~ op ~ " rect.width,
124                               this.height " ~ op ~ " rect.height);");
125             default:
126                 assert(0, "Unsupported Operation: " ~ op);
127         }
128     }
129     
130     /**
131      * Collapse this Rect. Means that the size is set to 0.
132      */
133     @nogc
134     void collapse() pure nothrow {
135         this.width = this.height = 0;
136     }
137     
138     /**
139      * Checks if this Rect is empty (if it's collapsed) with SDL_RectEmpty.
140      */
141     @nogc
142     bool isEmpty() const pure nothrow {
143         return this.width == 0 || this.height == 0;
144     }
145     
146     /**
147      * Returns an union of the given and this Rect.
148      */
149     @nogc
150     Rect getUnion(const Rect rect) const {
151         SDL_Rect a = void;
152         SDL_Rect b = void;
153         SDL_Rect c = void;
154 
155         SDL_UnionRect(_transfer(this, a), _transfer(rect, b), &c);
156 
157         return Rect(c.x, c.y, c.w, c.h);
158     }
159     
160     /**
161      * Checks whether this Rect contains the given coordinates.
162      */
163     @nogc
164     bool contains(const Vector2i vec) const pure nothrow {
165         return this.contains(vec.x, vec.y);
166     }
167     
168     /**
169      * Checks whether this Rect contains the given coordinates.
170      */
171     @nogc
172     bool contains(int x, int y) const pure nothrow {
173         return (x >= this.x) && (x < this.x + this.width) && (y >= this.y) && (y < this.y + this.height);
174     }
175     
176     /**
177      * opEquals: compares two rectangles on their coordinates and their size (but not explicit type).
178      */
179     @nogc
180     bool opEquals(const Rect rect) const pure nothrow {
181         return this.x == rect.x && this.y == rect.y && this.width == rect.width && this.height == rect.height;
182     }
183     
184     /**
185      * Checks whether this Rect intersects with an other.
186      * If, and the parameter 'overlap' isn't null,
187      * the colliding rectangle is stored there.
188      */
189     @nogc
190     bool intersects(const Rect rect, Rect* overlap = null) const {
191         SDL_Rect a = void;
192         SDL_Rect b = void;
193 
194         if (!overlap)
195             return SDL_HasIntersection(_transfer(this, a), _transfer(rect, b)) == SDL_TRUE;
196 
197         SDL_Rect c = void;
198 
199         immutable bool intersects = SDL_IntersectRect(_transfer(this, a), _transfer(rect, b), &c) == SDL_TRUE;
200 
201         overlap.x = c.x;
202         overlap.y = c.y;
203         overlap.width = c.w;
204         overlap.height = c.h;
205 
206         return intersects;
207     }
208     
209     /**
210      * Replace the current size.
211      */
212     @nogc
213     void setSize(uint width, uint height) pure nothrow {
214         this.width  = width;
215         this.height = height;
216     }
217 
218     /**
219      * Replace the current size.
220      */
221     @nogc
222     void setSize(const Size size) pure nothrow {
223         this.setSize(size.width, size.height);
224     }
225     
226     /**
227      * Returns the current size as Vector2
228      */
229     @nogc
230     Size getSize() const pure nothrow {
231         return Size(this.width, this.height);
232     }
233     
234     /**
235      * Increase current size.
236      */
237     @nogc
238     void increase(int width, int height) pure nothrow {
239         this.width += width;
240         this.height += height;
241     }
242     
243     /**
244      * Set a new position with coordinates.
245      */
246     @nogc
247     void setPosition(int x, int y) pure nothrow {
248         this.x = x;
249         this.y = y;
250     }
251     
252     /**
253      * Set a new position with a vector.
254      */
255     @nogc
256     void setPosition(const Vector2i position) pure nothrow {
257         this.setPosition(position.x, position.y);
258     }
259     
260     /**
261      * Returns the current position as Vector2
262      */
263     @nogc
264     Vector2i getPosition() const pure nothrow {
265         return Vector2i(this.x, this.y);
266     }
267     
268     /**
269      * Move the object.
270      */
271     @nogc
272     void move(const Vector2i vec) pure nothrow {
273         this.move(vec.x, vec.y);
274     }
275     
276     /**
277      * Move the object.
278      */
279     @nogc
280     void move(int x, int y) pure nothrow {
281         this.x += x;
282         this.y += y;
283     }
284 
285     /**
286      * Returns the position of the given Edge on this Rect.
287      */
288     @nogc
289     Vector2i getEdgePosition(Edge edge) const pure nothrow {
290         Vector2i pos;
291         final switch (edge) {
292             case Edge.Top:
293                 pos.x = this.x + (this.width / 2);
294                 pos.y = this.y;
295             break;
296             case Edge.Bottom:
297                 pos.x = this.x + (this.width / 2);
298                 pos.y = this.y + this.height;
299             break;
300             case Edge.Left:
301                 pos.x = this.x;
302                 pos.y = this.y + (this.height / 2);
303             break;
304             case Edge.Right:
305                 pos.x = this.x + this.width;
306                 pos.y = this.y + (this.height / 2);
307             break;
308             case Edge.TopLeft:
309                 pos.x = this.x;
310                 pos.y = this.y;
311             break;
312             case Edge.TopRight:
313                 pos.x = this.x + this.width;
314                 pos.y = this.y;
315             break;
316             case Edge.BottomLeft:
317                 pos.x = this.x;
318                 pos.y = this.y + this.height;
319             break;
320             case Edge.BottomRight:
321                 pos.x = this.x + this.width;
322                 pos.y = this.y + this.height;
323             break;
324         }
325 
326         return pos;
327     }
328 
329     /**
330      * Returns the center position of this Rect
331      */
332     @nogc
333     Vector2i getCenter() const pure nothrow {
334         return Vector2i(this.x + (this.width / 2), this.y + (this.height / 2));
335     }
336 }