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