1 /**
2   Scalar and integer 2D rect types.
3 
4   Copyright: Chris Jones
5   License: Boost Software License, Version 1.0
6   Authors: Chris Jones
7 */
8 
9 module dg2d.rect;
10 
11 import dg2d.scalar;
12 import dg2d.point;
13 import dg2d.misc;
14 import dg2d.path;
15 
16 import std.algorithm: among;
17 
18 /**
19   Integer 2D rectangle.
20  
21   This is typically used for specifying rectanges in exact pixel coordinates. Like
22   clip regions, or Window position, etc. 
23 */
24 
25 struct IRect
26 {
27     int left;
28     int top;
29     int right;
30     int bottom;
31 
32     /** Constructs a IRect with the spefcified coordinates */
33 
34     this(int left, int top, int right, int bottom)
35     {
36         this.left = left;
37         this.top = top;
38         this.right = right;
39         this.bottom = bottom;
40     }
41 
42     /** returns the width of the rectangle */
43 
44     int width()
45     {
46         return right-left;
47     }
48     
49     /** returns the height of the rectangle */
50 
51     int height()
52     {
53         return bottom-top;
54     }
55   
56     /** returns true if left > = right, or top >= bottom */
57 
58     bool isEmpty()
59     {
60         return ((left >= right) || (top >= bottom));
61     }
62 }
63 
64 /** Returns rect offset by x,y */
65 
66 IRect offset(IRect rect, int x, int y)
67 {
68     return IRect(rect.left+x, rect.top+y, rect.right+x, rect.bottom+y);
69 }
70 
71 /** Returns rect offset by p */
72 
73 IRect offset(IRect rect, IPoint p)
74 {
75     return IRect(rect.left+p.x, rect.top+p.y, rect.right+p.x, rect.bottom+p.y);
76 }
77 
78 /** Returns the insection of a and b */
79 
80 IRect intersect(IRect a, IRect b)
81 {
82     return IRect(
83         max(a.left, b.left), max(a.top, b.top),
84         min(a.right, b.right), min(a.bottom, b.bottom)
85         );
86 }
87 
88 /** Returns the union of a and b */
89 
90 IRect combine(IRect a, IRect b)
91 {
92     return IRect(
93         min(a.left, b.left), min(a.top, b.top),
94         max(a.right, b.right), max(a.bottom, b.bottom)
95         );
96 }
97 
98 /**
99   2D rectangle
100 */
101 
102 struct Rect
103 {
104     Scalar x0 = 0;
105     Scalar y0 = 0;
106     Scalar x1 = 0;
107     Scalar y1 = 0;
108 
109     /** Constructs a Rect with the spefcified coordinates */
110 
111     this(Scalar x0, Scalar y0, Scalar x1, Scalar y1)
112     {
113         this.x0 = x0;
114         this.y0 = y0;
115         this.x1 = x1;
116         this.y1 = y1;
117     }
118 
119     /** Returns the width of the rectangle */
120 
121     Scalar width()
122     {
123         return x1-x0;
124     }
125     
126     /** Returns the height of the rectangle */
127 
128     Scalar height()
129     {
130         return y1-y0;
131     }
132 
133     /** Returns the area of the rectangle */
134 
135     Scalar area()
136     {
137         return width*height;
138     }
139 
140     /** Returns the center of the rectangle */
141 
142     Point center()
143     {
144         return Point((x0+x1)/2,(y0+y1)/2);
145     }
146 
147     /** Returns true if (x0 >= x1) or (y0 >= y1) */
148 
149     bool isEmpty()
150     {
151         return ((x0 >= x1) || (y0 >= y1));
152     }
153 
154     /** operator overload for add, subtract or multiply. */
155 
156     Rect opBinary(string op)(Point rhs)
157         if (op.among!("+", "-", "*"))
158     {
159         mixin("return Rect(x0 "~op~" rhs.x, y0 "~op~"rhs.y, x1 "
160           ~op~" rhs.x, y1 "~op~"rhs.y);");
161     }
162 
163     /** operator overload for add, subtract or multiply. */
164 
165     Rect opBinary(string op)(Scalar[2] rhs)
166         if (op.among!("+", "-", "*"))
167     {
168         mixin("return Rect(x0 "~op~" rhs[0], y0 "~op~"rhs[1], x1 "
169           ~op~" rhs[0], y1 "~op~"rhs[1]);");
170     }
171 
172     /** operator overload for multiply */
173 
174     Rect opBinary(string op)(Scalar rhs)
175         if (op == "*")
176     {
177         mixin("return Rect(x0 "~op~" rhs, y0 "~op~"rhs, x1 "
178           ~op~" rhs, y1 "~op~"rhs);");
179     }
180 
181     /** Returns a PathIterator for traversing the rectangle. */
182 
183     auto asPath()
184     {
185         struct RectAsPath
186         {
187         public:
188             Point opIndex(size_t idx)
189             {
190                 assert(idx < 5);
191                 switch(idx)
192                 {
193                     case 0: return Point(m_rect.x0,m_rect.y0);
194                     case 1: return Point(m_rect.x0,m_rect.y1);
195                     case 2: return Point(m_rect.x1,m_rect.y1);
196                     case 3: return Point(m_rect.x1,m_rect.y0);
197                     case 4: return Point(m_rect.x0,m_rect.y0);
198                     default: assert(0);
199                 }
200             }
201             PathCmd cmd(size_t idx)
202             {
203                 if (idx == 0) return PathCmd.move;
204                 return PathCmd.line;
205             }
206             size_t length() { return 5; }
207             void* source() { return &this; }
208             bool inPlace() { return true; }      
209         private:
210             Rect* m_rect;
211         }
212 
213         return RectAsPath(&this);
214     }
215 }
216 
217 /**
218   Returns rect offset by x,y
219 */
220 
221 Rect offset(Rect rect, Scalar x, Scalar y)
222 {
223     return Rect(rect.x0 + x, rect.y0 + y, rect.x1 + x, rect.y1 + y);
224 }
225 
226 /**
227   Returns rect offset by point
228 */
229 
230 Rect offset(Rect rect, Point point)
231 {
232     return Rect(rect.x0 + point.x, rect.y0 + point.y, rect.x1 + point.x, rect.y1 + point.y);
233 }
234 
235 /**
236   Returns rect scaled by scale_x, scale_y
237 */
238 
239 Rect scale(Rect rect, Scalar scale_x, Scalar scale_y)
240 {
241     return Rect(rect.x0 * scale_x, rect.y0 * scale_y, rect.x1 * scale_x, rect.y1 * scale_y);
242 }
243 
244 /**
245   Returns rect scaled by scale_x, scale_y relative to focus_x,focus_y
246 */
247 
248 Rect scale(Rect rect, Scalar scale_x, Scalar scale_y,
249     Scalar focus_x, Scalar focus_y)
250 {
251     return Rect((rect.x0 - focus_x) * scale_x + focus_x,
252                 (rect.y0 - focus_y) * scale_y + focus_y,
253                 (rect.x1 - focus_x) * scale_x + focus_x,
254                 (rect.y1 - focus_y) * scale_y + focus_y);
255 }
256 
257 /**
258   Returns the intersection of two Rects
259 */
260 
261 Rect intersect(Rect a, Rect b)
262 {
263     return Rect(
264         max(a.x0, b.x0), max(a.y0, b.y0),
265         min(a.x1, b.x1), min(a.y1, b.y1)
266         );
267 }
268 
269 /**
270   Returns the union of two Rects
271 */
272 
273 Rect combine(Rect a, Rect b)
274 {
275     return Rect(
276         min(a.x0, b.x0), min(a.y0, b.y0),
277         max(a.x1, b.x1), max(a.y1, b.y1)
278         );
279 }
280 
281 /**
282   Returns rect inset by delta.
283 */
284 
285 Rect inset(Rect rect, Scalar delta)
286 {
287     Rect tmp = Rect(rect.x0 + delta, rect.y0 + delta, rect.x1 - delta, rect.y1 - delta);
288     if (tmp.x0 > tmp.x1) tmp.x0 = tmp.x1 = (tmp.x0+tmp.x1)/2;
289     if (tmp.y0 > tmp.y1) tmp.y0 = tmp.y1 = (tmp.y0+tmp.y1)/2;
290     return tmp;
291 }
292 
293 /** Returns rect outset by delta */
294 
295 Rect outset(Rect rect, Scalar delta)
296 {
297     return Rect(rect.x0 - delta, rect.y0 - delta, rect.x1 + delta, rect.y1 + delta);
298 }
299 
300 /** Returns rect ordered so x0 < x1 and y0 < y1 */
301 
302 Rect ordered(Rect rect)
303 {
304     return Rect(min(rect.x0,rect.x1), min(rect.y0,rect.y0),
305         max(rect.x0,rect.x1), max(rect.y0,rect.y1));
306 }