]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
(#938) Merge rect_layer and proto_rect together
[nothing.git] / src / game / level / level_editor / rect_layer.c
1 #include "game/camera.h"
2 #include "system/lt.h"
3 #include "system/stacktrace.h"
4 #include "system/nth_alloc.h"
5 #include "system/log.h"
6 #include "math/rect.h"
7 #include "color.h"
8 #include "rect_layer.h"
9 #include "dynarray.h"
10 #include "system/line_stream.h"
11 #include "color_picker.h"
12 #include "system/str.h"
13
14 #define RECT_LAYER_ID_MAX_SIZE 36
15 #define PROTO_AREA_THRESHOLD 10.0
16
17 typedef enum {
18     RECT_LAYER_IDLE = 0,
19     RECT_LAYER_PROTO
20 } RectLayerState;
21
22 /* TODO(#886): RectLayer does not allow to modify ids of Rects */
23 struct RectLayer {
24     Lt *lt;
25     RectLayerState state;
26     Dynarray *ids;
27     Dynarray *rects;
28     Dynarray *colors;
29     ColorPicker color_picker;
30     Vec begin, end;
31 };
32
33 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
34 {
35     LayerPtr layer = {
36         .type = LAYER_RECT,
37         .ptr = rect_layer
38     };
39     return layer;
40 }
41
42 RectLayer *create_rect_layer(void)
43 {
44     Lt *lt = create_lt();
45
46     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
47     if (layer == NULL) {
48         RETURN_LT(lt, NULL);
49     }
50     layer->lt = lt;
51
52     layer->ids = PUSH_LT(
53         lt,
54         create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
55         destroy_dynarray);
56     if (layer->ids == NULL) {
57         RETURN_LT(lt, NULL);
58     }
59
60     layer->rects = PUSH_LT(
61         lt,
62         create_dynarray(sizeof(Rect)),
63         destroy_dynarray);
64     if (layer->rects == NULL) {
65         RETURN_LT(lt, NULL);
66     }
67
68     layer->colors = PUSH_LT(
69         lt,
70         create_dynarray(sizeof(Color)),
71         destroy_dynarray);
72     if (layer->colors == NULL) {
73         RETURN_LT(lt, NULL);
74     }
75
76     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
77
78     return layer;
79 }
80
81 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
82 {
83     trace_assert(line_stream);
84
85     RectLayer *layer = create_rect_layer();
86     if (layer == NULL) {
87         return NULL;
88     }
89
90     const char *line = line_stream_next(line_stream);
91     if (line == NULL) {
92         RETURN_LT(layer->lt, NULL);
93     }
94
95     size_t count = 0;
96     if (sscanf(line, "%zu", &count) < 0) {
97         RETURN_LT(layer->lt, NULL);
98     }
99
100     for (size_t i = 0; i < count; ++i) {
101         line = line_stream_next(line_stream);
102         if (line == NULL) {
103             RETURN_LT(layer->lt, NULL);
104         }
105
106         char hex[7];
107         Rect rect;
108         char id[RECT_LAYER_ID_MAX_SIZE];
109
110         if (sscanf(line,
111                    "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
112                    id,
113                    &rect.x, &rect.y,
114                    &rect.w, &rect.h,
115                    hex) < 0) {
116             RETURN_LT(layer->lt, NULL);
117         }
118
119         Color color = hexstr(hex);
120
121         dynarray_push(layer->rects, &rect);
122         dynarray_push(layer->ids, id);
123         dynarray_push(layer->colors, &color);
124     }
125
126     return layer;
127 }
128
129 void destroy_rect_layer(RectLayer *layer)
130 {
131     trace_assert(layer);
132     RETURN_LT0(layer->lt);
133 }
134
135 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
136 {
137     trace_assert(layer);
138     trace_assert(camera);
139
140     const size_t n = dynarray_count(layer->rects);
141     Rect *rects = dynarray_data(layer->rects);
142     Color *colors = dynarray_data(layer->colors);
143
144     for (size_t i = 0; i < n; ++i) {
145         if (camera_fill_rect(
146                 camera,
147                 rects[i],
148                 color_scale(
149                     colors[i],
150                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
151             return -1;
152         }
153     }
154
155     const Color color = color_picker_rgba(&layer->color_picker);
156     if (layer->state == RECT_LAYER_PROTO) {
157         if (camera_fill_rect(camera, rect_from_points(layer->begin, layer->end), color) < 0) {
158             return -1;
159         }
160     }
161
162     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
163         return -1;
164     }
165
166     return 0;
167 }
168
169 int rect_layer_event(RectLayer *layer, const SDL_Event *event, const Camera *camera)
170 {
171     trace_assert(layer);
172     trace_assert(event);
173
174     int selected = 0;
175     if (color_picker_event(&layer->color_picker, event, &selected) < 0) {
176         return -1;
177     }
178
179     if (!selected) {
180         const Color color = color_picker_rgba(&layer->color_picker);
181         if (layer->state == RECT_LAYER_PROTO) {
182             // Active
183             switch (event->type) {
184             case SDL_MOUSEBUTTONUP: {
185                 switch (event->button.button) {
186                 case SDL_BUTTON_LEFT: {
187                     const Rect real_rect =
188                         rect_from_points(
189                             layer->begin,
190                             layer->end);
191                     const float area = real_rect.w * real_rect.h;
192
193                     if (area >= PROTO_AREA_THRESHOLD) {
194                         rect_layer_add_rect(layer, real_rect, color);
195                     } else {
196                         log_info("The area is too small %f. Such small box won't be created.\n", area);
197                     }
198                     layer->state = RECT_LAYER_IDLE;
199                 } break;
200                 }
201             } break;
202
203             case SDL_MOUSEMOTION: {
204                 layer->end = camera_map_screen(
205                     camera,
206                     event->motion.x,
207                     event->motion.y);
208             } break;
209             }
210         } else {
211             // Inactive
212             switch (event->type) {
213             case SDL_MOUSEBUTTONDOWN: {
214                 switch (event->button.button) {
215                 case SDL_BUTTON_LEFT: {
216                     layer->state = RECT_LAYER_PROTO;
217                     layer->begin = camera_map_screen(
218                         camera,
219                         event->button.x,
220                         event->button.y);
221                     layer->end = layer->begin;
222                 } break;
223                 }
224             } break;
225             }
226         }
227     }
228
229     return 0;
230 }
231
232 int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color)
233 {
234     trace_assert(layer);
235
236     if (dynarray_push(layer->rects, &rect) < 0) {
237         return -1;
238     }
239
240     if (dynarray_push(layer->colors, &color) < 0) {
241         return -1;
242     }
243
244     char id[RECT_LAYER_ID_MAX_SIZE];
245     for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
246         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
247     }
248     id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
249
250     if (dynarray_push(layer->ids, id)) {
251         return -1;
252     }
253
254     return 0;
255 }
256
257 int rect_layer_delete_rect_at(RectLayer *layer, Vec position)
258 {
259     trace_assert(layer);
260
261     const size_t n = dynarray_count(layer->rects);
262     Rect *rects = dynarray_data(layer->rects);
263
264     for (size_t i = 0; i < n; ++i) {
265         if (rect_contains_point(rects[i], position)) {
266             dynarray_delete_at(layer->rects, i);
267             dynarray_delete_at(layer->colors, i);
268             return 0;
269         }
270     }
271
272     return 0;
273 }
274
275 size_t rect_layer_count(const RectLayer *layer)
276 {
277     return dynarray_count(layer->rects);
278 }
279
280 const Rect *rect_layer_rects(const RectLayer *layer)
281 {
282     return dynarray_data(layer->rects);
283 }
284
285 const Color *rect_layer_colors(const RectLayer *layer)
286 {
287     return dynarray_data(layer->colors);
288 }
289
290 const char *rect_layer_ids(const RectLayer *layer)
291 {
292     return dynarray_data(layer->ids);
293 }
294
295 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
296 {
297     trace_assert(layer);
298     trace_assert(filedump);
299
300     size_t n = dynarray_count(layer->ids);
301     char *ids = dynarray_data(layer->ids);
302     Rect *rects = dynarray_data(layer->rects);
303     Color *colors = dynarray_data(layer->colors);
304
305     fprintf(filedump, "%zd\n", n);
306     for (size_t i = 0; i < n; ++i) {
307         fprintf(filedump, "%s %f %f %f %f ",
308                 ids + RECT_LAYER_ID_MAX_SIZE * i,
309                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
310         color_hex_to_stream(colors[i], filedump);
311         fprintf(filedump, "\n");
312     }
313
314     return 0;
315 }