]> git.lizzy.rs Git - nothing.git/blob - src/math/rect.c
(#1142) Bot-left resize snapping
[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 (Rect_side 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 = {
109         .p1 = {0, 0},
110         .p2 = {0, 0}
111     };
112
113     switch (side) {
114     case RECT_SIDE_LEFT:
115         result.p1.x = x1;
116         result.p1.y = y1;
117         result.p2.x = x1;
118         result.p2.y = y2;
119         break;
120     case RECT_SIDE_RIGHT:
121         result.p1.x = x2;
122         result.p1.y = y1;
123         result.p2.x = x2;
124         result.p2.y = y2;
125         break;
126     case RECT_SIDE_TOP:
127         result.p1.x = x1;
128         result.p1.y = y1;
129         result.p2.x = x2;
130         result.p2.y = y1;
131         break;
132     case RECT_SIDE_BOTTOM:
133         result.p1.x = x1;
134         result.p1.y = y2;
135         result.p2.x = x2;
136         result.p2.y = y2;
137         break;
138     default: {}
139     }
140
141     return result;
142 }
143
144 Rect rect_from_point(Vec2f p, float w, float h)
145 {
146     Rect result = {
147         .x = p.x,
148         .y = p.y,
149         .w = w,
150         .h = h
151     };
152
153     return result;
154 }
155
156 int rect_contains_point(Rect rect, Vec2f p)
157 {
158     return rect.x <= p.x && p.x <= rect.x + rect.w
159         && rect.y <= p.y && p.y <= rect.y + rect.h;
160 }
161
162 SDL_Rect rect_for_sdl(Rect rect)
163 {
164     const SDL_Rect result = {
165         .x = (int) roundf(rect.x),
166         .y = (int) roundf(rect.y),
167         .w = (int) roundf(rect.w),
168         .h = (int) roundf(rect.h)
169     };
170
171     return result;
172 }
173
174 Vec2f rect_center(Rect rect)
175 {
176     return vec(rect.x + rect.w * 0.5f,
177                rect.y + rect.h * 0.5f);
178 }
179
180 Vec2f rect_snap(Rect pivot, Rect *r)
181 {
182     const Vec2f pivot_c = rect_center(pivot);
183     const Vec2f r_c = rect_center(*r);
184
185     const float sx = r_c.x < pivot_c.x ? -1.0f : 1.0f;
186     const float sy = r_c.y < pivot_c.y ? -1.0f : 1.0f;
187     const float cx = pivot_c.x + sx * (pivot.w + r->w) * 0.5f;
188     const float cy = pivot_c.y + sy * (pivot.h + r->h) * 0.5f;
189
190     if (fabsf(cx - r_c.x) < fabsf(cy - r_c.y)) {
191         *r = rect(cx - r->w * 0.5f, r->y, r->w, r->h);
192         return vec(0.0f, 1.0f);
193     } else {
194         *r = rect(r->x, cy - r->h * 0.5f, r->w, r->h);
195         return vec(1.0f, 0.0f);
196     }
197 }
198
199 Vec2f rect_impulse(Rect *r1, Rect *r2)
200 {
201     trace_assert(r1);
202     trace_assert(r2);
203
204     const Vec2f c1 = rect_center(*r1);
205     const Vec2f c2 = rect_center(*r2);
206     const Rect overlap = rects_overlap_area(*r1, *r2);
207     const Vec2f overlap_center = rect_center(overlap);
208     const float dx = overlap_center.x;
209     const float dy = overlap_center.y;
210     const float sx = c1.x < c2.x ? 1.0f : -1.0f;
211     const float sy = c1.y < c2.y ? 1.0f : -1.0f;
212     const float cx1 = dx - sx * r1->w * 0.5f;
213     const float cy1 = dy - sy * r1->h * 0.5f;
214     const float cx2 = dx + sx * r2->w * 0.5f;
215     const float cy2 = dy + sy * r2->h * 0.5f;
216
217     if (vec_sqr_norm(vec_sum(vec(cx1, c1.y), vec_neg(vec(cx2, c2.y)))) <
218         vec_sqr_norm(vec_sum(vec(c1.x, cy1), vec_neg(vec(c2.x, cy2))))) {
219         r1->x = cx1 - r1->w * 0.5f;
220         r2->x = cx2 - r2->w * 0.5f;
221         return vec(0.0f, 1.0f);
222     } else {
223         r1->y = cy1 - r1->h * 0.5f;
224         r2->y = cy2 - r2->h * 0.5f;
225         return vec(1.0f, 0.0f);
226     }
227 }
228
229 Rect horizontal_thicc_line(float x1, float x2, float y, float thiccness)
230 {
231     if (x1 > x2) {
232         float t = x1;
233         x1 = x2;
234         x2 = t;
235     }
236
237     return rect(
238         x1 - thiccness * 0.5f,
239         y - thiccness * 0.5f,
240         x2 - x1 + thiccness,
241         thiccness);
242 }
243
244 Rect vertical_thicc_line(float y1, float y2, float x, float thiccness)
245 {
246     if (y1 > y2) {
247         float t = y1;
248         y1 = y2;
249         y2 = t;
250     }
251
252     return rect(
253         x - thiccness * 0.5f,
254         y1 - thiccness * 0.5f,
255         thiccness,
256         y2 - y1 + thiccness);
257 }