]> git.lizzy.rs Git - nothing.git/blob - src/math/rect.c
Merge pull request #1089 from tsoding/vec
[nothing.git] / src / math / rect.c
1 #include <SDL.h>
2 #include <math.h>
3 #include <string.h>
4
5 #include "rect.h"
6 #include "system/stacktrace.h"
7
8 Rect rect(float x, float y, float w, float h)
9 {
10     const Rect result = {
11         .x = x,
12         .y = y,
13         .w = w,
14         .h = h
15     };
16
17     return result;
18 }
19
20 Rect rect_from_vecs(Vec2f position, Vec2f size)
21 {
22     return rect(position.x, position.y, size.x, size.y);
23 }
24
25 Rect rect_from_points(Vec2f p1, Vec2f p2)
26 {
27     return rect_from_vecs(
28         vec(fminf(p1.x, p2.x),
29             fminf(p1.y, p2.y)),
30         vec(fabsf(p1.x - p2.x),
31             fabsf(p1.y - p2.y)));
32 }
33
34 Rect rect_from_sdl(const SDL_Rect *rect)
35 {
36     const Rect result = {
37         .x = (float) rect->x,
38         .y = (float) rect->y,
39         .w = (float) rect->w,
40         .h = (float) rect->h
41     };
42
43     return result;
44 }
45
46 Rect rects_overlap_area(Rect rect1, Rect rect2)
47 {
48     float x1 = fmaxf(rect1.x, rect2.x);
49     float y1 = fmaxf(rect1.y, rect2.y);
50     float x2 = fminf(rect1.x + rect1.w, rect2.x + rect2.w);
51     float y2 = fminf(rect1.y + rect1.h, rect2.y + rect2.h);
52
53     Rect result = {
54         .x = x1,
55         .y = y1,
56         .w = fmaxf(0.0f, x2 - x1),
57         .h = fmaxf(0.0f, y2 - y1)
58     };
59     return result;
60 }
61
62 int rects_overlap(Rect rect1, Rect rect2)
63 {
64     return rect1.x + rect1.w > rect2.x
65         && rect2.x + rect2.w > rect1.x
66         && rect2.y + rect2.h > rect1.y
67         && rect1.y + rect1.h > rect2.y;
68 }
69
70 float line_length(Line line)
71 {
72     float dx = line.p1.x - line.p2.x;
73     float dy = line.p1.y - line.p2.y;
74     return sqrtf(dx * dx + dy * dy);
75 }
76
77 void rect_object_impact(Rect object, Rect obstacle, int *sides)
78 {
79     trace_assert(sides);
80
81     Rect int_area = rects_overlap_area(object, obstacle);
82
83     if (int_area.w * int_area.h > 0.0f) {
84         for (int side = 0; side < RECT_SIDE_N; ++side) {
85             Line object_side = rect_side(object, side);
86             Line int_side = rect_side(int_area, side);
87
88             if (line_length(int_side) > 10.0f) {
89                 sides[side] = sides[side] ||
90                     (fabs(object_side.p1.x - object_side.p2.x) < 1e-6
91                      && fabs(object_side.p1.x - int_side.p1.x) < 1e-6
92                      && fabs(object_side.p1.x - int_side.p2.x) < 1e-6)
93                     || (fabs(object_side.p1.y - object_side.p2.y) < 1e-6
94                         && fabs(object_side.p1.y - int_side.p1.y) < 1e-6
95                         && fabs(object_side.p1.y - int_side.p2.y) < 1e-6);
96             }
97         }
98     }
99 }
100
101 Line rect_side(Rect rect, Rect_side side)
102 {
103     const float x1 = rect.x;
104     const float y1 = rect.y;
105     const float x2 = rect.x + rect.w;
106     const float y2 = rect.y + rect.h;
107
108     Line result = { 0 };
109
110     switch (side) {
111     case RECT_SIDE_LEFT:
112         result.p1.x = x1;
113         result.p1.y = y1;
114         result.p2.x = x1;
115         result.p2.y = y2;
116         break;
117     case RECT_SIDE_RIGHT:
118         result.p1.x = x2;
119         result.p1.y = y1;
120         result.p2.x = x2;
121         result.p2.y = y2;
122         break;
123     case RECT_SIDE_TOP:
124         result.p1.x = x1;
125         result.p1.y = y1;
126         result.p2.x = x2;
127         result.p2.y = y1;
128         break;
129     case RECT_SIDE_BOTTOM:
130         result.p1.x = x1;
131         result.p1.y = y2;
132         result.p2.x = x2;
133         result.p2.y = y2;
134         break;
135     default: {}
136     }
137
138     return result;
139 }
140
141 Rect rect_from_point(Vec2f p, float w, float h)
142 {
143     Rect result = {
144         .x = p.x,
145         .y = p.y,
146         .w = w,
147         .h = h
148     };
149
150     return result;
151 }
152
153 int rect_contains_point(Rect rect, Vec2f p)
154 {
155     return rect.x <= p.x && p.x <= rect.x + rect.w
156         && rect.y <= p.y && p.y <= rect.y + rect.h;
157 }
158
159 SDL_Rect rect_for_sdl(Rect rect)
160 {
161     const SDL_Rect result = {
162         .x = (int) roundf(rect.x),
163         .y = (int) roundf(rect.y),
164         .w = (int) roundf(rect.w),
165         .h = (int) roundf(rect.h)
166     };
167
168     return result;
169 }
170
171 Vec2f rect_center(Rect rect)
172 {
173     return vec(rect.x + rect.w * 0.5f,
174                rect.y + rect.h * 0.5f);
175 }
176
177 Vec2f rect_snap(Rect pivot, Rect *r)
178 {
179     const Vec2f pivot_c = rect_center(pivot);
180     const Vec2f r_c = rect_center(*r);
181
182     const float sx = r_c.x < pivot_c.x ? -1.0f : 1.0f;
183     const float sy = r_c.y < pivot_c.y ? -1.0f : 1.0f;
184     const float cx = pivot_c.x + sx * (pivot.w + r->w) * 0.5f;
185     const float cy = pivot_c.y + sy * (pivot.h + r->h) * 0.5f;
186
187     if (fabsf(cx - r_c.x) < fabsf(cy - r_c.y)) {
188         *r = rect(cx - r->w * 0.5f, r->y, r->w, r->h);
189         return vec(0.0f, 1.0f);
190     } else {
191         *r = rect(r->x, cy - r->h * 0.5f, r->w, r->h);
192         return vec(1.0f, 0.0f);
193     }
194 }
195
196 Vec2f rect_impulse(Rect *r1, Rect *r2)
197 {
198     trace_assert(r1);
199     trace_assert(r2);
200
201     const Vec2f c1 = rect_center(*r1);
202     const Vec2f c2 = rect_center(*r2);
203     const Rect overlap = rects_overlap_area(*r1, *r2);
204     const Vec2f overlap_center = rect_center(overlap);
205     const float dx = overlap_center.x;
206     const float dy = overlap_center.y;
207     const float sx = c1.x < c2.x ? 1.0f : -1.0f;
208     const float sy = c1.y < c2.y ? 1.0f : -1.0f;
209     const float cx1 = dx - sx * r1->w * 0.5f;
210     const float cy1 = dy - sy * r1->h * 0.5f;
211     const float cx2 = dx + sx * r2->w * 0.5f;
212     const float cy2 = dy + sy * r2->h * 0.5f;
213
214     if (vec_sqr_norm(vec_sum(vec(cx1, c1.y), vec_neg(vec(cx2, c2.y)))) <
215         vec_sqr_norm(vec_sum(vec(c1.x, cy1), vec_neg(vec(c2.x, cy2))))) {
216         r1->x = cx1 - r1->w * 0.5f;
217         r2->x = cx2 - r2->w * 0.5f;
218         return vec(0.0f, 1.0f);
219     } else {
220         r1->y = cy1 - r1->h * 0.5f;
221         r2->y = cy2 - r2->h * 0.5f;
222         return vec(1.0f, 0.0f);
223     }
224 }
225
226 Rect horizontal_thicc_line(float x1, float x2, float y, float thiccness)
227 {
228     if (x1 > x2) {
229         float t = x1;
230         x1 = x2;
231         x2 = t;
232     }
233
234     return rect(
235         x1 - thiccness * 0.5f,
236         y - thiccness * 0.5f,
237         x2 - x1 + thiccness,
238         thiccness);
239 }
240
241 Rect vertical_thicc_line(float y1, float y2, float x, float thiccness)
242 {
243     if (y1 > y2) {
244         float t = y1;
245         y1 = y2;
246         y2 = t;
247     }
248
249     return rect(
250         x - thiccness * 0.5f,
251         y1 - thiccness * 0.5f,
252         thiccness,
253         y2 - y1 + thiccness);
254 }