]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
(#886) Make RectLayer display the ids
[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 RECT_LAYER_SELECTION_THICCNESS 5.0f
16 #define CREATE_AREA_THRESHOLD 10.0
17
18 typedef enum {
19     RECT_LAYER_IDLE = 0,
20     RECT_LAYER_CREATE,
21     RECT_LAYER_RESIZE,
22     RECT_LAYER_MOVE
23 } RectLayerState;
24
25 /* TODO(#886): RectLayer does not allow to modify ids of Rects */
26 struct RectLayer {
27     Lt *lt;
28     RectLayerState state;
29     Dynarray *ids;
30     Dynarray *rects;
31     Dynarray *colors;
32     ColorPicker color_picker;
33     Vec create_begin;
34     Vec create_end;
35     int selection;
36     Vec move_anchor;
37 };
38
39 static int rect_layer_rect_at(RectLayer *layer, Vec position)
40 {
41     trace_assert(layer);
42
43     const size_t n = dynarray_count(layer->rects);
44     Rect *rects = dynarray_data(layer->rects);
45
46     for (size_t i = 0; i < n; ++i) {
47         if (rect_contains_point(rects[i], position)) {
48             return (int) i;
49         }
50     }
51
52     return -1;
53 }
54
55 static int rect_layer_delete_rect_at(RectLayer *layer, size_t i)
56 {
57     trace_assert(layer);
58
59     dynarray_delete_at(layer->rects, i);
60     dynarray_delete_at(layer->colors, i);
61     dynarray_delete_at(layer->ids, i);
62
63     return 0;
64 }
65
66 static Rect rect_layer_resize_anchor(const RectLayer *layer, size_t i)
67 {
68     Rect *rects = dynarray_data(layer->rects);
69     return rect(rects[i].x + rects[i].w,
70                 rects[i].y + rects[i].h,
71                 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
72                 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
73 }
74
75 static int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color)
76 {
77     trace_assert(layer);
78
79     if (dynarray_push(layer->rects, &rect) < 0) {
80         return -1;
81     }
82
83     if (dynarray_push(layer->colors, &color) < 0) {
84         return -1;
85     }
86
87     char id[RECT_LAYER_ID_MAX_SIZE];
88     for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
89         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
90     }
91     id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
92
93     if (dynarray_push(layer->ids, id)) {
94         return -1;
95     }
96
97     return 0;
98 }
99
100
101 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
102 {
103     LayerPtr layer = {
104         .type = LAYER_RECT,
105         .ptr = rect_layer
106     };
107     return layer;
108 }
109
110 RectLayer *create_rect_layer(void)
111 {
112     Lt *lt = create_lt();
113
114     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
115     if (layer == NULL) {
116         RETURN_LT(lt, NULL);
117     }
118     layer->lt = lt;
119
120     layer->ids = PUSH_LT(
121         lt,
122         create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
123         destroy_dynarray);
124     if (layer->ids == NULL) {
125         RETURN_LT(lt, NULL);
126     }
127
128     layer->rects = PUSH_LT(
129         lt,
130         create_dynarray(sizeof(Rect)),
131         destroy_dynarray);
132     if (layer->rects == NULL) {
133         RETURN_LT(lt, NULL);
134     }
135
136     layer->colors = PUSH_LT(
137         lt,
138         create_dynarray(sizeof(Color)),
139         destroy_dynarray);
140     if (layer->colors == NULL) {
141         RETURN_LT(lt, NULL);
142     }
143
144     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
145     layer->selection = -1;
146
147     return layer;
148 }
149
150 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
151 {
152     trace_assert(line_stream);
153
154     RectLayer *layer = create_rect_layer();
155     if (layer == NULL) {
156         return NULL;
157     }
158
159     const char *line = line_stream_next(line_stream);
160     if (line == NULL) {
161         RETURN_LT(layer->lt, NULL);
162     }
163
164     size_t count = 0;
165     if (sscanf(line, "%zu", &count) < 0) {
166         RETURN_LT(layer->lt, NULL);
167     }
168
169     for (size_t i = 0; i < count; ++i) {
170         line = line_stream_next(line_stream);
171         if (line == NULL) {
172             RETURN_LT(layer->lt, NULL);
173         }
174
175         char hex[7];
176         Rect rect;
177         char id[RECT_LAYER_ID_MAX_SIZE];
178
179         if (sscanf(line,
180                    "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
181                    id,
182                    &rect.x, &rect.y,
183                    &rect.w, &rect.h,
184                    hex) < 0) {
185             RETURN_LT(layer->lt, NULL);
186         }
187
188         Color color = hexstr(hex);
189
190         dynarray_push(layer->rects, &rect);
191         dynarray_push(layer->ids, id);
192         dynarray_push(layer->colors, &color);
193     }
194
195     return layer;
196 }
197
198 void destroy_rect_layer(RectLayer *layer)
199 {
200     trace_assert(layer);
201     RETURN_LT0(layer->lt);
202 }
203
204 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
205 {
206     trace_assert(layer);
207     trace_assert(camera);
208
209     const size_t n = dynarray_count(layer->rects);
210     Rect *rects = dynarray_data(layer->rects);
211     Color *colors = dynarray_data(layer->colors);
212     const char *ids = dynarray_data(layer->ids);
213
214     for (size_t i = 0; i < n; ++i) {
215         if (layer->selection == (int) i) {
216             if (active) {
217                 const Color color = color_invert(colors[i]);
218
219                 if (camera_fill_rect(
220                         camera,
221                         // TODO(#943): thiccness of RectLayer selection should be probably based on zoom
222                         rect_scale(rects[i], RECT_LAYER_SELECTION_THICCNESS),
223                         color) < 0) {
224                     return -1;
225                 }
226
227                 if (camera_fill_rect(
228                         camera,
229                         rect_layer_resize_anchor(layer, i),
230                         color) < 0) {
231                     return -1;
232                 }
233             }
234         }
235
236         if (camera_fill_rect(
237                 camera,
238                 rects[i],
239                 color_scale(
240                     colors[i],
241                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
242             return -1;
243         }
244
245         if (camera_render_text(
246                 camera,
247                 ids + i * RECT_LAYER_ID_MAX_SIZE,
248                 vec(3.0f, 3.0f),
249                 color_invert(colors[i]),
250                 rect_position(rects[i])) < 0) {
251             return -1;
252         }
253     }
254
255     const Color color = color_picker_rgba(&layer->color_picker);
256     if (layer->state == RECT_LAYER_CREATE) {
257         if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
258             return -1;
259         }
260     }
261
262     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
263         return -1;
264     }
265
266     return 0;
267 }
268
269 int rect_layer_event(RectLayer *layer, const SDL_Event *event, const Camera *camera)
270 {
271     trace_assert(layer);
272     trace_assert(event);
273
274     int selected = 0;
275     if (color_picker_event(&layer->color_picker, event, &selected) < 0) {
276         return -1;
277     }
278
279     if (selected) {
280         return 0;
281     }
282
283     const Color color = color_picker_rgba(&layer->color_picker);
284
285     switch (layer->state) {
286     case RECT_LAYER_CREATE: {
287         switch (event->type) {
288         case SDL_MOUSEBUTTONUP: {
289             switch (event->button.button) {
290             case SDL_BUTTON_LEFT: {
291                 const Rect real_rect =
292                     rect_from_points(
293                         layer->create_begin,
294                         layer->create_end);
295                 const float area = real_rect.w * real_rect.h;
296
297                 if (area >= CREATE_AREA_THRESHOLD) {
298                     rect_layer_add_rect(layer, real_rect, color);
299                 } else {
300                     log_info("The area is too small %f. Such small box won't be created.\n", area);
301                 }
302                 layer->state = RECT_LAYER_IDLE;
303             } break;
304             }
305         } break;
306
307         case SDL_MOUSEMOTION: {
308             layer->create_end = camera_map_screen(
309                 camera,
310                 event->motion.x,
311                 event->motion.y);
312         } break;
313         }
314     } break;
315
316     case RECT_LAYER_RESIZE: {
317         switch (event->type) {
318         case SDL_MOUSEMOTION: {
319             Rect *rects = dynarray_data(layer->rects);
320             trace_assert(layer->selection >= 0);
321             rects[layer->selection] = rect_from_points(
322                 vec(rects[layer->selection].x, rects[layer->selection].y),
323                 vec_sum(
324                     camera_map_screen(
325                         camera,
326                         event->button.x,
327                         event->button.y),
328                     vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
329                         RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
330         } break;
331
332         case SDL_MOUSEBUTTONUP: {
333             layer->state = RECT_LAYER_IDLE;
334         } break;
335         }
336     } break;
337
338     case RECT_LAYER_IDLE: {
339         switch (event->type) {
340         case SDL_MOUSEBUTTONDOWN: {
341             switch (event->button.button) {
342             case SDL_BUTTON_LEFT: {
343                 Point position = camera_map_screen(
344                     camera,
345                     event->button.x,
346                     event->button.y);
347                 int rect_at_position =
348                     rect_layer_rect_at(layer, position);
349
350                 if (rect_at_position >= 0) {
351                     Rect *rects = dynarray_data(layer->rects);
352                     layer->selection = rect_at_position;
353                     layer->state = RECT_LAYER_MOVE;
354                     layer->move_anchor =
355                         vec_sub(
356                             position,
357                             vec(
358                                 rects[layer->selection].x,
359                                 rects[layer->selection].y));
360                 } else if (layer->selection >= 0 && rect_contains_point(
361                                rect_layer_resize_anchor(
362                                    layer,
363                                    (size_t)layer->selection),
364                                position)) {
365                     layer->state = RECT_LAYER_RESIZE;
366                 } else {
367                     layer->selection = rect_at_position;
368
369                     if (layer->selection < 0) {
370                         layer->state = RECT_LAYER_CREATE;
371                         layer->create_begin = position;
372                         layer->create_end = position;
373                     }
374                 }
375             } break;
376             }
377         } break;
378
379         case SDL_KEYDOWN: {
380             switch (event->key.keysym.sym) {
381             case SDLK_DELETE: {
382                 if (layer->selection >= 0) {
383                     rect_layer_delete_rect_at(layer, (size_t) layer->selection);
384                     layer->selection = -1;
385                 }
386             } break;
387             }
388         } break;
389         }
390     } break;
391
392     case RECT_LAYER_MOVE: {
393         switch (event->type) {
394         case SDL_MOUSEMOTION: {
395             Point position = vec_sub(
396                 camera_map_screen(
397                     camera,
398                     event->button.x,
399                     event->button.y),
400                 layer->move_anchor);
401
402             Rect *rects = dynarray_data(layer->rects);
403
404             trace_assert(layer->selection >= 0);
405
406             rects[layer->selection].x = position.x;
407             rects[layer->selection].y = position.y;
408         } break;
409
410         case SDL_MOUSEBUTTONUP: {
411             layer->state = RECT_LAYER_IDLE;
412         } break;
413         }
414     } break;
415     }
416
417     return 0;
418 }
419
420 size_t rect_layer_count(const RectLayer *layer)
421 {
422     return dynarray_count(layer->rects);
423 }
424
425 const Rect *rect_layer_rects(const RectLayer *layer)
426 {
427     return dynarray_data(layer->rects);
428 }
429
430 const Color *rect_layer_colors(const RectLayer *layer)
431 {
432     return dynarray_data(layer->colors);
433 }
434
435 const char *rect_layer_ids(const RectLayer *layer)
436 {
437     return dynarray_data(layer->ids);
438 }
439
440 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
441 {
442     trace_assert(layer);
443     trace_assert(filedump);
444
445     size_t n = dynarray_count(layer->ids);
446     char *ids = dynarray_data(layer->ids);
447     Rect *rects = dynarray_data(layer->rects);
448     Color *colors = dynarray_data(layer->colors);
449
450     fprintf(filedump, "%zd\n", n);
451     for (size_t i = 0; i < n; ++i) {
452         fprintf(filedump, "%s %f %f %f %f ",
453                 ids + RECT_LAYER_ID_MAX_SIZE * i,
454                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
455         color_hex_to_stream(colors[i], filedump);
456         fprintf(filedump, "\n");
457     }
458
459     return 0;
460 }