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