1 #pragma once
2 
3 /** @file the_Foundation/rect.h  2D integer rectangle.
4 
5 @authors Copyright (c) 2019 Jaakko Keränen <jaakko.keranen@iki.fi>
6 
7 @par License
8 
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11 
12 1. Redistributions of source code must retain the above copyright notice, this
13    list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright notice,
15    this list of conditions and the following disclaimer in the documentation
16    and/or other materials provided with the distribution.
17 
18 <small>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</small>
28 */
29 
30 #include "range.h"
31 #include "vec2.h"
32 
33 iDeclareType(Rect)
34 
35 iBeginPublic
36 
37 struct Impl_Rect {
38     iInt2 pos;
39     iInt2 size;
40 };
41 
42 iDeclareType(Stream)
iDeclareTypeSerialization(Rect)43 iDeclareTypeSerialization(Rect)
44 
45 iLocalDef iRect zero_Rect(void) { return (iRect){ zero_I2(), zero_I2() }; }
46 
init_Rect(int x,int y,int width,int height)47 iLocalDef iRect init_Rect(int x, int y, int width, int height) {
48     return (iRect){ init_I2(x, y), init_I2(width, height) };
49 }
50 
initv_Rect(const int * v)51 iLocalDef iRect initv_Rect(const int *v) {
52     return (iRect){ initv_I2(v), initv_I2(v + 2) };
53 }
54 
initCorners_Rect(const iInt2 topLeft,const iInt2 bottomRight)55 iLocalDef iRect initCorners_Rect(const iInt2 topLeft, const iInt2 bottomRight) {
56     return (iRect){ topLeft, sub_I2(bottomRight, topLeft) };
57 }
58 
initCentered_Rect(const iInt2 center,const iInt2 size)59 iLocalDef iRect initCentered_Rect(const iInt2 center, const iInt2 size) {
60     return (iRect){ sub_I2(center, divi_I2(size, 2)), size };
61 }
62 
initSize_Rect(int width,int height)63 iLocalDef iRect initSize_Rect(int width, int height) {
64     return init_Rect(0, 0, width, height);
65 }
66 
left_Rect(const iRect d)67 iLocalDef int   left_Rect   (const iRect d) { return d.pos.x; }
right_Rect(const iRect d)68 iLocalDef int   right_Rect  (const iRect d) { return d.pos.x + d.size.x; }
top_Rect(const iRect d)69 iLocalDef int   top_Rect    (const iRect d) { return d.pos.y; }
bottom_Rect(const iRect d)70 iLocalDef int   bottom_Rect (const iRect d) { return d.pos.y + d.size.y; }
width_Rect(const iRect d)71 iLocalDef int   width_Rect  (const iRect d) { return d.size.x; }
height_Rect(const iRect d)72 iLocalDef int   height_Rect (const iRect d) { return d.size.y; }
area_Rect(const iRect d)73 iLocalDef int   area_Rect   (const iRect d) { return d.size.x * d.size.y; }
mid_Rect(const iRect d)74 iLocalDef iInt2 mid_Rect    (const iRect d) { return add_I2(d.pos, divi_I2(d.size, 2)); }
75 
topLeft_Rect(const iRect d)76 iLocalDef iInt2 topLeft_Rect(const iRect d) { return d.pos; }
77 
topMid_Rect(const iRect d)78 iLocalDef iInt2 topMid_Rect(const iRect d) {
79     return init_I2(d.pos.x + d.size.x / 2, d.pos.y);
80 }
81 
topRight_Rect(const iRect d)82 iLocalDef iInt2 topRight_Rect(const iRect d) {
83     return init_I2(right_Rect(d), d.pos.y);
84 }
85 
bottomLeft_Rect(const iRect d)86 iLocalDef iInt2 bottomLeft_Rect(const iRect d) {
87     return init_I2(d.pos.x, bottom_Rect(d));
88 }
89 
bottomMid_Rect(const iRect d)90 iLocalDef iInt2 bottomMid_Rect(const iRect d) {
91     return init_I2(d.pos.x + d.size.x / 2, bottom_Rect(d));
92 }
93 
bottomRight_Rect(const iRect d)94 iLocalDef iInt2 bottomRight_Rect(const iRect d) {
95     return add_I2(d.pos, d.size);
96 }
97 
xSpan_Rect(const iRect d)98 iLocalDef iRangei xSpan_Rect(const iRect d) {
99     return (iRangei){ left_Rect(d), right_Rect(d) };
100 }
101 
ySpan_Rect(const iRect d)102 iLocalDef iRangei ySpan_Rect(const iRect d) {
103     return (iRangei){ top_Rect(d), bottom_Rect(d) };
104 }
105 
contains_Rect(const iRect d,const iInt2 pos)106 iLocalDef iBool contains_Rect(const iRect d, const iInt2 pos) {
107     const iInt2 br = bottomRight_Rect(d);
108     return pos.x >= d.pos.x && pos.y >= d.pos.y && pos.x < br.x && pos.y < br.y;
109 }
110 
containsRect_Rect(const iRect d,const iRect other)111 iLocalDef iBool containsRect_Rect(const iRect d, const iRect other) {
112     const iInt2 br = sub_I2(bottomRight_Rect(other), one_I2());
113     return contains_Rect(d, topLeft_Rect(other)) &&
114            contains_Rect(d, init_I2(br.x, top_Rect(other))) && contains_Rect(d, br) &&
115            contains_Rect(d, init_I2(left_Rect(other), br.y));
116 }
117 
isOverlapping_Rect(const iRect d,const iRect other)118 iLocalDef iBool isOverlapping_Rect(const iRect d, const iRect other) {
119     /* Overlaps unless any one of the edges makes it impossible. */
120     return !(other.pos.x >= right_Rect(d) || other.pos.y >= bottom_Rect(d) ||
121              right_Rect(other) <= d.pos.x || bottom_Rect(other) <= d.pos.y);
122 }
123 
isEmpty_Rect(const iRect d)124 iLocalDef iBool isEmpty_Rect(const iRect d) {
125     return prod_I2(d.size) == 0;
126 }
127 
equal_Rect(const iRect d,const iRect other)128 iLocalDef iBool equal_Rect(const iRect d, const iRect other) {
129     return isEqual_I2(d.pos, other.pos) && isEqual_I2(d.size, other.size);
130 }
131 
union_Rect(const iRect d,const iRect other)132 iLocalDef iRect union_Rect(const iRect d, const iRect other) {
133     if (isEmpty_Rect(d)) {
134         return other;
135     }
136     if (isEmpty_Rect(other)) {
137         return d;
138     }
139     const iInt2 tl = min_I2(d.pos, other.pos);
140     const iInt2 br = max_I2(bottomRight_Rect(d), bottomRight_Rect(other));
141     return (iRect){ tl, sub_I2(br, tl) };
142 }
143 
intersect_Rect(const iRect d,const iRect other)144 iLocalDef iRect intersect_Rect(const iRect d, const iRect other) {
145     if (!isOverlapping_Rect(d, other)) {
146         return zero_Rect();
147     }
148     const iInt2 tl = max_I2(d.pos, other.pos);
149     const iInt2 br = min_I2(bottomRight_Rect(d), bottomRight_Rect(other));
150     return (iRect){ tl, sub_I2(br, tl) };
151 }
152 
153 void    expand_Rect         (iRect *, iInt2 value);
154 void    adjustEdges_Rect    (iRect *, int top, int right, int bottom, int left);
155 iInt2   random_Rect         (iRect);
156 iInt2   edgePos_Rect        (iRect, int pos);
157 iInt2   randomEdgePos_Rect  (iRect); // not a corner
158 
shrink_Rect(iRect * d,iInt2 value)159 iLocalDef void shrink_Rect  (iRect *d, iInt2 value) { expand_Rect(d, neg_I2(value)); }
160 
expanded_Rect(iRect d,iInt2 value)161 iLocalDef iRect expanded_Rect(iRect d, iInt2 value) {
162     expand_Rect(&d, value);
163     return d;
164 }
165 
shrunk_Rect(iRect d,iInt2 value)166 iLocalDef iRect shrunk_Rect(iRect d, iInt2 value) {
167     expand_Rect(&d, neg_I2(value));
168     return d;
169 }
170 
adjusted_Rect(const iRect d,iInt2 topLeft,iInt2 bottomRight)171 iLocalDef iRect adjusted_Rect(const iRect d, iInt2 topLeft, iInt2 bottomRight) {
172     return initCorners_Rect(add_I2(d.pos, topLeft), add_I2(bottomRight_Rect(d), bottomRight));
173 }
174 
moved_Rect(const iRect d,iInt2 offset)175 iLocalDef iRect moved_Rect(const iRect d, iInt2 offset) {
176     return (iRect){ add_I2(d.pos, offset), d.size };
177 }
178 
179 iDeclareConstIterator(Rect, iRect)
180 
181 struct ConstIteratorImpl_Rect {
182     iBool value; // position is valid
183     iInt2 pos;
184     iRect rect;
185 };
186 
187 #define iForRectRadius(iter, center, radius, body) { \
188     const iInt2 center_ForRadius_ = (center); \
189     const int radius_ForRadius_ = (radius); \
190     const iRect rect_ForRadius_ = initCentered_Rect(center_ForRadius_, init1_I2(2 * radius_ForRadius_ + 1)); \
191     iConstForEach(Rect, iter, rect_ForRadius_) { \
192         if (dist_I2(center_ForRadius_, iter.pos) + .5f <= radius_ForRadius_) { body } \
193     } \
194 }
195 
196 iEndPublic
197