]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
55f0dce3aab60a8551de82b0dec8e052c4b3f354
[nothing.git] / src / game / level / level_editor / rect_layer.c
1 #include <errno.h>
2
3 #include "game/camera.h"
4 #include "system/lt.h"
5 #include "system/stacktrace.h"
6 #include "system/nth_alloc.h"
7 #include "system/log.h"
8 #include "math/rect.h"
9 #include "color.h"
10 #include "rect_layer.h"
11 #include "dynarray.h"
12 #include "system/line_stream.h"
13 #include "color_picker.h"
14 #include "system/str.h"
15 #include "ui/edit_field.h"
16 #include "undo_history.h"
17 #include "game/level/action.h"
18
19 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
20 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
21 #define CREATE_AREA_THRESHOLD 10.0
22
23 static int clipboard = 0;
24 static Rect clipboard_rect;
25 static Color clipboard_color;
26
27 typedef enum {
28     RECT_LAYER_IDLE = 0,
29     RECT_LAYER_CREATE,
30     // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
31     RECT_LAYER_RESIZE,
32     RECT_LAYER_MOVE,
33     RECT_LAYER_ID_RENAME,
34     RECT_LAYER_RECOLOR
35 } RectLayerState;
36
37 struct RectLayer {
38     Lt *lt;
39     RectLayerState state;
40     Dynarray *ids;
41     Dynarray *rects;
42     Dynarray *colors;
43     Dynarray *actions;
44     ColorPicker color_picker;
45     Vec2f create_begin;
46     Vec2f create_end;
47     int selection;
48     Vec2f move_anchor;
49     Edit_field *id_edit_field;
50     Color inter_color;
51     Rect inter_rect;
52     int id_name_counter;
53     const char *id_name_prefix;
54 };
55
56 typedef enum {
57     UNDO_ADD,
58     UNDO_DELETE,
59     UNDO_UPDATE,
60     UNDO_SWAP
61 } UndoType;
62
63 // Delete, Update
64 typedef struct {
65     UndoType type;
66     RectLayer *layer;
67     size_t index;
68     Rect rect;
69     Color color;
70     Action action;
71     char id[ENTITY_MAX_ID_SIZE];
72 } UndoElementContext;
73
74 // Add
75 typedef struct {
76     UndoType type;
77     RectLayer *layer;
78     size_t index;
79 } UndoAddContext;
80
81 // Swap
82 typedef struct {
83     UndoType type;
84     RectLayer *layer;
85     size_t index1;
86     size_t index2;
87 } UndoSwapContext;
88
89 typedef union {
90     UndoType type;
91     UndoAddContext add;
92     UndoElementContext element;
93     UndoSwapContext swap;
94 } UndoContext;
95
96 static
97 UndoContext create_undo_add_context(RectLayer *layer, size_t index)
98 {
99     trace_assert(layer);
100     trace_assert(index < dynarray_count(layer->rects));
101
102     UndoContext undo_context;
103     undo_context.add.type = UNDO_ADD;
104     undo_context.add.layer = layer;
105     undo_context.add.index = index;
106     return undo_context;
107 }
108
109 static
110 UndoContext create_undo_element_context(RectLayer *layer)
111 {
112     trace_assert(layer);
113     size_t index = (size_t) layer->selection;
114     trace_assert(index < dynarray_count(layer->rects));
115
116     UndoContext undo_context;
117     undo_context.element.layer = layer;
118     undo_context.element.index = index;
119     dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
120     dynarray_copy_to(layer->colors, &undo_context.element.color, index);
121     dynarray_copy_to(layer->ids, undo_context.element.id, index);
122     dynarray_copy_to(layer->actions, &undo_context.element.action, index);
123     return undo_context;
124 }
125
126 static
127 UndoContext create_undo_update_context(RectLayer *rect_layer)
128 {
129     UndoContext undo_context = create_undo_element_context(rect_layer);
130     undo_context.type = UNDO_UPDATE;
131     return undo_context;
132 }
133
134 static
135 UndoContext create_undo_delete_context(RectLayer *rect_layer)
136 {
137     UndoContext undo_context = create_undo_element_context(rect_layer);
138     undo_context.type = UNDO_DELETE;
139     return undo_context;
140 }
141
142 static
143 UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
144 {
145     UndoContext undo_context;
146     undo_context.swap.type = UNDO_SWAP;
147     undo_context.swap.layer = rect_layer;
148     undo_context.swap.index1 = index1;
149     undo_context.swap.index2 = index2;
150     return undo_context;
151 }
152
153 static
154 void rect_layer_undo(void *context, size_t context_size)
155 {
156     trace_assert(context);
157     trace_assert(sizeof(UndoContext) == context_size);
158
159     UndoContext *undo_context = context;
160
161     switch (undo_context->type) {
162     case UNDO_ADD: {
163         RectLayer *layer = undo_context->add.layer;
164         dynarray_delete_at(layer->rects, undo_context->add.index);
165         dynarray_delete_at(layer->colors, undo_context->add.index);
166         dynarray_delete_at(layer->ids, undo_context->add.index);
167         dynarray_delete_at(layer->actions, undo_context->add.index);
168         layer->selection = -1;
169     } break;
170
171     case UNDO_DELETE: {
172         RectLayer *layer = undo_context->element.layer;
173         dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
174         dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
175         dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
176         dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
177         layer->selection = -1;
178     } break;
179
180     case UNDO_UPDATE: {
181         RectLayer *layer = undo_context->element.layer;
182         dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
183         dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
184         dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
185         dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
186     } break;
187
188     case UNDO_SWAP: {
189         RectLayer *layer = undo_context->element.layer;
190         dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
191         dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
192         dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
193         dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
194     } break;
195     }
196 }
197
198 #define UNDO_PUSH(HISTORY, CONTEXT)                                     \
199     do {                                                                \
200         UndoContext context = (CONTEXT);                                \
201         undo_history_push(                                              \
202             HISTORY,                                                    \
203             rect_layer_undo,                                            \
204             &context,                                                   \
205             sizeof(context));                                           \
206     } while(0)
207
208 static int rect_layer_add_rect(RectLayer *layer,
209                                Rect rect,
210                                Color color,
211                                UndoHistory *undo_history)
212 {
213     trace_assert(layer);
214
215     if (dynarray_push(layer->rects, &rect) < 0) {
216         return -1;
217     }
218
219     if (dynarray_push(layer->colors, &color) < 0) {
220         return -1;
221     }
222
223     char id[ENTITY_MAX_ID_SIZE];
224     snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
225              layer->id_name_prefix,
226              layer->id_name_counter++);
227     if (dynarray_push(layer->ids, id)) {
228         return -1;
229     }
230
231     UNDO_PUSH(
232         undo_history,
233         create_undo_add_context(
234             layer,
235             dynarray_count(layer->rects) - 1));
236
237     return 0;
238 }
239
240 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
241 {
242     trace_assert(layer);
243
244     int n = (int) dynarray_count(layer->rects);
245     Rect *rects = dynarray_data(layer->rects);
246
247     for (int i = n - 1; i >= 0; --i) {
248         if (rect_contains_point(rects[i], position)) {
249             return (int) i;
250         }
251     }
252
253     return -1;
254 }
255
256 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
257                                      UndoHistory *undo_history)
258 {
259     trace_assert(layer);
260     trace_assert(a < dynarray_count(layer->rects));
261     trace_assert(b < dynarray_count(layer->rects));
262
263     dynarray_swap(layer->rects, a, b);
264     dynarray_swap(layer->colors, a, b);
265     dynarray_swap(layer->ids, a, b);
266     dynarray_swap(layer->actions, a, b);
267
268     UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b));
269 }
270
271 static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
272 {
273     const Rect overlay_rect =
274         rect_scale(
275             camera_rect(camera, boundary_rect),
276             RECT_LAYER_SELECTION_THICCNESS * 0.5f);
277
278     return rect(
279         overlay_rect.x + overlay_rect.w,
280         overlay_rect.y + overlay_rect.h,
281         RECT_LAYER_SELECTION_THICCNESS * 2.0f,
282         RECT_LAYER_SELECTION_THICCNESS * 2.0f);
283 }
284
285 static int rect_layer_delete_rect_at(RectLayer *layer,
286                                      size_t i,
287                                      UndoHistory *undo_history)
288 {
289     trace_assert(layer);
290
291     UNDO_PUSH(undo_history, create_undo_delete_context(layer));
292
293     dynarray_delete_at(layer->rects, i);
294     dynarray_delete_at(layer->colors, i);
295     dynarray_delete_at(layer->ids, i);
296     dynarray_delete_at(layer->actions, i);
297
298     return 0;
299 }
300
301 static int rect_layer_event_idle(RectLayer *layer,
302                                  const SDL_Event *event,
303                                  const Camera *camera,
304                                  UndoHistory *undo_history)
305 {
306     trace_assert(layer);
307     trace_assert(event);
308     trace_assert(camera);
309
310     int color_changed = 0;
311     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
312         return -1;
313     }
314
315     if (color_changed) {
316         if (layer->selection >= 0) {
317             dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
318             layer->state = RECT_LAYER_RECOLOR;
319         }
320         return 0;
321     }
322
323     switch (event->type) {
324     case SDL_MOUSEBUTTONDOWN: {
325         switch (event->button.button) {
326         case SDL_BUTTON_LEFT: {
327             Vec2f position = camera_map_screen(
328                 camera,
329                 event->button.x,
330                 event->button.y);
331             int rect_at_position =
332                 rect_layer_rect_at(layer, position);
333
334             Rect *rects = dynarray_data(layer->rects);
335             Color *colors = dynarray_data(layer->colors);
336
337             if (layer->selection >= 0 && rect_contains_point(
338                     rect_layer_resize_anchor(
339                         camera,
340                         rects[layer->selection]),
341                     vec(
342                         (float) event->button.x,
343                         (float) event->button.y))) {
344                 layer->state = RECT_LAYER_RESIZE;
345                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
346             } else if (rect_at_position >= 0) {
347                 layer->selection = rect_at_position;
348                 layer->state = RECT_LAYER_MOVE;
349                 layer->move_anchor =
350                     vec_sub(
351                         position,
352                         vec(
353                             rects[layer->selection].x,
354                             rects[layer->selection].y));
355                 layer->color_picker =
356                     create_color_picker_from_rgba(colors[rect_at_position]);
357
358                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
359             } else {
360                 layer->selection = rect_at_position;
361
362                 if (layer->selection < 0) {
363                     layer->state = RECT_LAYER_CREATE;
364                     layer->create_begin = position;
365                     layer->create_end = position;
366                 }
367             }
368         } break;
369         }
370     } break;
371
372     case SDL_KEYDOWN: {
373         switch (event->key.keysym.sym) {
374         case SDLK_UP: {
375             if ((event->key.keysym.mod & KMOD_SHIFT)
376                 && (layer->selection >= 0)
377                 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
378                 rect_layer_swap_elements(
379                     layer,
380                     (size_t) layer->selection,
381                     (size_t) layer->selection + 1,
382                     undo_history);
383                 layer->selection++;
384             }
385         } break;
386
387         case SDLK_DOWN: {
388             if ((event->key.keysym.mod & KMOD_SHIFT)
389                 && (layer->selection > 0)
390                 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
391                 rect_layer_swap_elements(
392                     layer,
393                     (size_t) layer->selection,
394                     (size_t) layer->selection - 1,
395                     undo_history);
396                 layer->selection--;
397             }
398         } break;
399
400         case SDLK_DELETE: {
401             if (layer->selection >= 0) {
402                 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
403                 layer->selection = -1;
404             }
405         } break;
406
407         case SDLK_F2: {
408             if (layer->selection >= 0) {
409                 const char *ids = dynarray_data(layer->ids);
410                 Color *colors = dynarray_data(layer->colors);
411
412                 edit_field_restyle(
413                     layer->id_edit_field,
414                     RECT_LAYER_ID_LABEL_SIZE,
415                     color_invert(colors[layer->selection]));
416
417                 layer->state = RECT_LAYER_ID_RENAME;
418                 edit_field_replace(
419                     layer->id_edit_field,
420                     ids + layer->selection * ENTITY_MAX_ID_SIZE);
421                 SDL_StartTextInput();
422             }
423         } break;
424
425         case SDLK_c: {
426             if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
427                 clipboard = 1;
428                 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
429                 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
430             }
431         } break;
432
433         case SDLK_v: {
434             if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
435                 int x, y;
436                 SDL_GetMouseState(&x, &y);
437                 Vec2f position = camera_map_screen(camera, x, y);
438
439                 rect_layer_add_rect(
440                     layer,
441                     rect(position.x, position.y,
442                          clipboard_rect.w, clipboard_rect.h),
443                     clipboard_color,
444                     undo_history);
445             }
446         } break;
447         }
448     } break;
449     }
450
451     return 0;
452 }
453
454 static int rect_layer_event_create(RectLayer *layer,
455                                    const SDL_Event *event,
456                                    const Camera *camera,
457                                    UndoHistory *undo_history)
458 {
459     trace_assert(layer);
460     trace_assert(event);
461     trace_assert(camera);
462
463     switch (event->type) {
464     case SDL_MOUSEBUTTONUP: {
465         switch (event->button.button) {
466         case SDL_BUTTON_LEFT: {
467             const Rect real_rect =
468                 rect_from_points(
469                     layer->create_begin,
470                     layer->create_end);
471             const float area = real_rect.w * real_rect.h;
472
473             if (area >= CREATE_AREA_THRESHOLD) {
474                 rect_layer_add_rect(
475                     layer,
476                     real_rect,
477                     color_picker_rgba(&layer->color_picker),
478                     undo_history);
479             } else {
480                 log_info("The area is too small %f. Such small box won't be created.\n", area);
481             }
482             layer->state = RECT_LAYER_IDLE;
483         } break;
484         }
485     } break;
486
487     case SDL_MOUSEMOTION: {
488         layer->create_end = camera_map_screen(
489             camera,
490             event->motion.x,
491             event->motion.y);
492     } break;
493     }
494     return 0;
495 }
496
497 static int rect_layer_event_resize(RectLayer *layer,
498                                    const SDL_Event *event,
499                                    const Camera *camera,
500                                    UndoHistory *undo_history)
501 {
502     trace_assert(layer);
503     trace_assert(event);
504     trace_assert(camera);
505     trace_assert(layer->selection >= 0);
506
507     switch (event->type) {
508     case SDL_MOUSEMOTION: {
509         layer->inter_rect = rect_from_points(
510             vec(layer->inter_rect.x, layer->inter_rect.y),
511             vec_sum(
512                 camera_map_screen(
513                     camera,
514                     event->button.x,
515                     event->button.y),
516                 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
517                     RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
518     } break;
519
520     case SDL_MOUSEBUTTONUP: {
521         layer->state = RECT_LAYER_IDLE;
522         UNDO_PUSH(undo_history, create_undo_update_context(layer));
523         dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
524     } break;
525     }
526
527     return 0;
528 }
529
530 static int rect_layer_event_move(RectLayer *layer,
531                                  const SDL_Event *event,
532                                  const Camera *camera,
533                                  UndoHistory *undo_history)
534 {
535     trace_assert(layer);
536     trace_assert(event);
537     trace_assert(camera);
538     trace_assert(layer->selection >= 0);
539
540     Rect *rects = dynarray_data(layer->rects);
541
542     switch (event->type) {
543     case SDL_MOUSEMOTION: {
544         Vec2f position = vec_sub(
545             camera_map_screen(
546                 camera,
547                 event->button.x,
548                 event->button.y),
549             layer->move_anchor);
550
551         trace_assert(layer->selection >= 0);
552
553         layer->inter_rect.x = position.x;
554         layer->inter_rect.y = position.y;
555     } break;
556
557     case SDL_MOUSEBUTTONUP: {
558         layer->state = RECT_LAYER_IDLE;
559
560         float distance = vec_length(
561             vec_sub(rect_position(layer->inter_rect),
562                     rect_position(rects[layer->selection])));
563
564         if (distance > 1e-6) {
565             UNDO_PUSH(undo_history, create_undo_update_context(layer));
566             dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
567         }
568     } break;
569     }
570     return 0;
571 }
572
573 static int rect_layer_event_id_rename(RectLayer *layer,
574                                       const SDL_Event *event,
575                                       const Camera *camera,
576                                       UndoHistory *undo_history)
577 {
578     trace_assert(layer);
579     trace_assert(event);
580     trace_assert(camera);
581     trace_assert(layer->selection >= 0);
582
583     switch (event->type) {
584     case SDL_KEYDOWN: {
585         switch (event->key.keysym.sym) {
586         case SDLK_RETURN: {
587             UNDO_PUSH(undo_history, create_undo_update_context(layer));
588
589             char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
590             memset(id, 0, ENTITY_MAX_ID_SIZE);
591             memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
592             layer->state = RECT_LAYER_IDLE;
593             SDL_StopTextInput();
594         } break;
595
596         case SDLK_ESCAPE: {
597             layer->state = RECT_LAYER_IDLE;
598             SDL_StopTextInput();
599         } break;
600         }
601     } break;
602     }
603
604     return edit_field_event(layer->id_edit_field, event);
605 }
606
607 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
608 {
609     LayerPtr layer = {
610         .type = LAYER_RECT,
611         .ptr = rect_layer
612     };
613     return layer;
614 }
615
616 RectLayer *create_rect_layer(const char *id_name_prefix)
617 {
618     Lt *lt = create_lt();
619
620     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
621     if (layer == NULL) {
622         RETURN_LT(lt, NULL);
623     }
624     layer->lt = lt;
625
626     layer->ids = PUSH_LT(
627         lt,
628         create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
629         destroy_dynarray);
630     if (layer->ids == NULL) {
631         RETURN_LT(lt, NULL);
632     }
633
634     layer->rects = PUSH_LT(
635         lt,
636         create_dynarray(sizeof(Rect)),
637         destroy_dynarray);
638     if (layer->rects == NULL) {
639         RETURN_LT(lt, NULL);
640     }
641
642     layer->colors = PUSH_LT(
643         lt,
644         create_dynarray(sizeof(Color)),
645         destroy_dynarray);
646     if (layer->colors == NULL) {
647         RETURN_LT(lt, NULL);
648     }
649
650     layer->actions = PUSH_LT(
651         lt,
652         create_dynarray(sizeof(Action)),
653         destroy_dynarray);
654     if (layer->actions == NULL) {
655         RETURN_LT(lt, NULL);
656     }
657
658     layer->id_edit_field = PUSH_LT(
659         lt,
660         create_edit_field(
661             RECT_LAYER_ID_LABEL_SIZE,
662             COLOR_BLACK),
663         destroy_edit_field);
664     if (layer->id_edit_field == NULL) {
665         RETURN_LT(lt, NULL);
666     }
667
668     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
669     layer->selection = -1;
670     layer->id_name_prefix = id_name_prefix;
671
672     return layer;
673 }
674
675 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
676 {
677     trace_assert(line_stream);
678
679     RectLayer *layer = create_rect_layer(id_name_prefix);
680     if (layer == NULL) {
681         return NULL;
682     }
683
684     const char *line = line_stream_next(line_stream);
685     if (line == NULL) {
686         RETURN_LT(layer->lt, NULL);
687     }
688
689     size_t count = 0;
690     if (sscanf(line, "%zu", &count) < 0) {
691         RETURN_LT(layer->lt, NULL);
692     }
693
694     for (size_t i = 0; i < count; ++i) {
695         line = line_stream_next(line_stream);
696         if (line == NULL) {
697             RETURN_LT(layer->lt, NULL);
698         }
699
700         char hex[7];
701         Rect rect;
702         char id[ENTITY_MAX_ID_SIZE];
703
704         int n = 0;
705         if (sscanf(line,
706                    "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
707                    id,
708                    &rect.x, &rect.y,
709                    &rect.w, &rect.h,
710                    hex, &n) == EOF) {
711             log_fail("%s\n", strerror(errno));
712             RETURN_LT(layer->lt, NULL);
713         }
714         line += n;
715
716         Color color = hexstr(hex);
717         dynarray_push(layer->rects, &rect);
718         dynarray_push(layer->ids, id);
719         dynarray_push(layer->colors, &color);
720
721         Action action = {0};
722
723         if (sscanf(line, "%d%n", (int*)&action.type, &n) != EOF) {
724             line += n;
725             switch (action.type) {
726             case ACTION_HIDE_LABEL: {
727                 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.label_id) == EOF) {
728                     log_fail("%s\n", strerror(errno));
729                     RETURN_LT(layer->lt, NULL);
730                 }
731             } break;
732             default: {}
733             }
734         }
735
736         dynarray_push(layer->actions, &action);
737     }
738
739     return layer;
740 }
741
742 void destroy_rect_layer(RectLayer *layer)
743 {
744     trace_assert(layer);
745     RETURN_LT0(layer->lt);
746 }
747
748 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
749 {
750     trace_assert(layer);
751     trace_assert(camera);
752
753     const size_t n = dynarray_count(layer->rects);
754     Rect *rects = dynarray_data(layer->rects);
755     Color *colors = dynarray_data(layer->colors);
756     const char *ids = dynarray_data(layer->ids);
757
758     // The Rectangles
759     for (size_t i = 0; i < n; ++i) {
760         Rect rect = rects[i];
761         Color color = colors[i];
762
763         if (layer->selection == (int) i) {
764             if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
765                 rect = layer->inter_rect;
766             }
767
768             if (layer->state == RECT_LAYER_RECOLOR) {
769                 color = layer->inter_color;
770             }
771         }
772
773         if (camera_fill_rect(
774                 camera,
775                 rect,
776                 color_scale(
777                     color,
778                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
779             return -1;
780         }
781
782         // Selection Overlay
783         if (active && (size_t) layer->selection == i) {
784             const Rect overlay_rect =
785                 rect_scale(
786                     camera_rect(camera, rect),
787                     RECT_LAYER_SELECTION_THICCNESS * 0.5f);
788             const Color overlay_color = color_invert(color);
789
790             // Main Rectangle
791             if (camera_fill_rect(
792                     camera,
793                     rect,
794                     color_scale(
795                         color,
796                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
797                 return -1;
798             }
799
800             if (camera_draw_thicc_rect_screen(
801                     camera,
802                     overlay_rect,
803                     overlay_color,
804                     RECT_LAYER_SELECTION_THICCNESS) < 0) {
805                 return -1;
806             }
807
808             // Rectangle Id
809             if (layer->state == RECT_LAYER_ID_RENAME) {
810                 // ID renaming Edit Field
811                 if (edit_field_render_world(
812                         layer->id_edit_field,
813                         camera,
814                         rect_position(rect)) < 0) {
815                     return -1;
816                 }
817             } else {
818                 // Id text
819                 if (camera_render_text(
820                         camera,
821                         ids + layer->selection * ENTITY_MAX_ID_SIZE,
822                         RECT_LAYER_ID_LABEL_SIZE,
823                         color_invert(color),
824                         rect_position(rect)) < 0) {
825                     return -1;
826                 }
827             }
828
829             // Resize Anchor
830             if (camera_fill_rect_screen(
831                     camera,
832                     rect_layer_resize_anchor(camera, rect),
833                     overlay_color) < 0) {
834                 return -1;
835             }
836         }
837     }
838
839     // Proto Rectangle
840     const Color color = color_picker_rgba(&layer->color_picker);
841     if (layer->state == RECT_LAYER_CREATE) {
842         if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
843             return -1;
844         }
845     }
846
847     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
848         return -1;
849     }
850
851     return 0;
852 }
853
854 static
855 int rect_layer_event_recolor(RectLayer *layer,
856                              const SDL_Event *event,
857                              const Camera *camera,
858                              UndoHistory *undo_history)
859 {
860     trace_assert(layer);
861     trace_assert(event);
862     trace_assert(camera);
863     trace_assert(undo_history);
864     trace_assert(layer->selection >= 0);
865
866     int color_changed = 0;
867     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
868         return -1;
869     }
870
871     if (color_changed) {
872         layer->inter_color = color_picker_rgba(&layer->color_picker);
873
874         if (!color_picker_drag(&layer->color_picker)) {
875             UNDO_PUSH(undo_history, create_undo_update_context(layer));
876             dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
877             layer->state = RECT_LAYER_IDLE;
878         }
879     }
880
881     return 0;
882 }
883
884 int rect_layer_event(RectLayer *layer,
885                      const SDL_Event *event,
886                      const Camera *camera,
887                      UndoHistory *undo_history)
888 {
889     trace_assert(layer);
890     trace_assert(event);
891     trace_assert(undo_history);
892
893     switch (layer->state) {
894     case RECT_LAYER_IDLE:
895         return rect_layer_event_idle(layer, event, camera, undo_history);
896
897     case RECT_LAYER_CREATE:
898         return rect_layer_event_create(layer, event, camera, undo_history);
899
900     case RECT_LAYER_RESIZE:
901         return rect_layer_event_resize(layer, event, camera, undo_history);
902
903     case RECT_LAYER_MOVE:
904         return rect_layer_event_move(layer, event, camera, undo_history);
905
906     case RECT_LAYER_ID_RENAME:
907         return rect_layer_event_id_rename(layer, event, camera, undo_history);
908
909     case RECT_LAYER_RECOLOR:
910         return rect_layer_event_recolor(layer, event, camera, undo_history);
911     }
912
913     return 0;
914 }
915
916 size_t rect_layer_count(const RectLayer *layer)
917 {
918     return dynarray_count(layer->rects);
919 }
920
921 const Rect *rect_layer_rects(const RectLayer *layer)
922 {
923     return dynarray_data(layer->rects);
924 }
925
926 const Color *rect_layer_colors(const RectLayer *layer)
927 {
928     return dynarray_data(layer->colors);
929 }
930
931 const char *rect_layer_ids(const RectLayer *layer)
932 {
933     return dynarray_data(layer->ids);
934 }
935
936 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
937 {
938     trace_assert(layer);
939     trace_assert(filedump);
940
941     size_t n = dynarray_count(layer->ids);
942     char *ids = dynarray_data(layer->ids);
943     Rect *rects = dynarray_data(layer->rects);
944     Color *colors = dynarray_data(layer->colors);
945     Action *actions = dynarray_data(layer->actions);
946
947     fprintf(filedump, "%zd\n", n);
948     for (size_t i = 0; i < n; ++i) {
949         fprintf(filedump, "%s %f %f %f %f ",
950                 ids + ENTITY_MAX_ID_SIZE * i,
951                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
952         color_hex_to_stream(colors[i], filedump);
953
954         switch (actions[i].type) {
955         case ACTION_HIDE_LABEL: {
956             fprintf(filedump, " %d %.*s ",
957                     (int)actions[i].type,
958                     ENTITY_MAX_ID_SIZE, actions[i].label_id);
959         } break;
960         default: {}
961         }
962
963         fprintf(filedump, "\n");
964     }
965
966     return 0;
967 }
968
969 const Action *rect_layer_actions(const RectLayer *layer)
970 {
971     return dynarray_data(layer->actions);
972 }