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