]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
(#164) Make layer translation units more unity build friendly
[nothing.git] / src / game / level / level_editor / rect_layer.c
1 #include <float.h>
2 #include <errno.h>
3
4 #include "game/camera.h"
5 #include "system/lt.h"
6 #include "system/stacktrace.h"
7 #include "system/nth_alloc.h"
8 #include "system/log.h"
9 #include "math/rect.h"
10 #include "color.h"
11 #include "rect_layer.h"
12 #include "dynarray.h"
13 #include "system/line_stream.h"
14 #include "color_picker.h"
15 #include "system/str.h"
16 #include "ui/edit_field.h"
17 #include "undo_history.h"
18 #include "game/level/action.h"
19 #include "action_picker.h"
20 #include "game.h"
21
22 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
23 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
24 #define CREATE_AREA_THRESHOLD 10.0
25 #define RECT_LAYER_GRID_ROWS 3
26 #define RECT_LAYER_GRID_COLUMNS 4
27
28 static int rect_clipboard = 0;
29 static Rect rect_clipboard_rect;
30 static Color rect_clipboard_color;
31
32 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
33     0,                         // [0]
34     CURSOR_STYLE_RESIZE_VERT,  // [1]
35     CURSOR_STYLE_RESIZE_HORIS, // [2]
36     CURSOR_STYLE_RESIZE_DIAG1, // [3]
37     CURSOR_STYLE_RESIZE_VERT,  // [4]
38     0,                         // [5]
39     CURSOR_STYLE_RESIZE_DIAG2, // [6]
40     0,                         // [7]
41     CURSOR_STYLE_RESIZE_HORIS, // [8]
42     CURSOR_STYLE_RESIZE_DIAG2, // [9]
43     0,                         // [10]
44     0,                         // [11]
45     CURSOR_STYLE_RESIZE_DIAG1  // [12]
46 };
47
48 typedef enum {
49     RECT_LAYER_IDLE = 0,
50     RECT_LAYER_CREATE,
51     RECT_LAYER_RESIZE,
52     RECT_LAYER_MOVE,
53     RECT_LAYER_ID_RENAME,
54     RECT_LAYER_RECOLOR
55 } RectLayerState;
56
57 struct RectLayer {
58     Lt *lt;
59     RectLayerState state;
60     int resize_mask;
61     Dynarray *ids;
62     Dynarray *rects;
63     Dynarray *colors;
64     Dynarray *actions;
65     ColorPicker color_picker;
66     ActionPicker action_picker;
67     Vec2f create_begin;
68     Vec2f create_end;
69     int selection;
70     Vec2f move_anchor;          // The mouse offset from the left-top
71                                 // corner of the rect during moving it
72     Edit_field *id_edit_field;
73     Color inter_color;
74     Rect inter_rect;
75     int id_name_counter;
76     const char *id_name_prefix;
77     Grid *grid;
78     Cursor *cursor;
79
80     int snapping_enabled;
81 };
82
83 typedef enum {
84     RECT_UNDO_ADD,
85     RECT_UNDO_DELETE,
86     RECT_UNDO_UPDATE,
87     RECT_UNDO_SWAP
88 } RectUndoType;
89
90 // Delete, Update
91 typedef struct {
92     RectUndoType type;
93     RectLayer *layer;
94     size_t index;
95     Rect rect;
96     Color color;
97     Action action;
98     char id[ENTITY_MAX_ID_SIZE];
99 } UndoElementContext;
100
101 // Add
102 typedef struct {
103     RectUndoType type;
104     RectLayer *layer;
105     size_t index;
106 } UndoAddContext;
107
108 // Swap
109 typedef struct {
110     RectUndoType type;
111     RectLayer *layer;
112     size_t index1;
113     size_t index2;
114 } UndoSwapContext;
115
116 typedef union {
117     RectUndoType type;
118     UndoAddContext add;
119     UndoElementContext element;
120     UndoSwapContext swap;
121 } RectUndoContext;
122
123 static
124 RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
125 {
126     trace_assert(layer);
127     trace_assert(index < dynarray_count(layer->rects));
128
129     RectUndoContext undo_context;
130     undo_context.add.type = RECT_UNDO_ADD;
131     undo_context.add.layer = layer;
132     undo_context.add.index = index;
133     return undo_context;
134 }
135
136 static
137 RectUndoContext create_rect_undo_element_context(RectLayer *layer)
138 {
139     trace_assert(layer);
140     size_t index = (size_t) layer->selection;
141     trace_assert(index < dynarray_count(layer->rects));
142
143     RectUndoContext undo_context;
144     undo_context.element.layer = layer;
145     undo_context.element.index = index;
146     dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
147     dynarray_copy_to(layer->colors, &undo_context.element.color, index);
148     dynarray_copy_to(layer->ids, undo_context.element.id, index);
149     dynarray_copy_to(layer->actions, &undo_context.element.action, index);
150     return undo_context;
151 }
152
153 static
154 RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer)
155 {
156     RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
157     undo_context.type = RECT_UNDO_UPDATE;
158     return undo_context;
159 }
160
161 static
162 RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer)
163 {
164     RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
165     undo_context.type = RECT_UNDO_DELETE;
166     return undo_context;
167 }
168
169 static
170 RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
171 {
172     RectUndoContext undo_context;
173     undo_context.swap.type = RECT_UNDO_SWAP;
174     undo_context.swap.layer = rect_layer;
175     undo_context.swap.index1 = index1;
176     undo_context.swap.index2 = index2;
177     return undo_context;
178 }
179
180 static
181 void rect_layer_undo(void *context, size_t context_size)
182 {
183     trace_assert(context);
184     trace_assert(sizeof(RectUndoContext) == context_size);
185
186     RectUndoContext *undo_context = context;
187
188     switch (undo_context->type) {
189     case RECT_UNDO_ADD: {
190         RectLayer *layer = undo_context->add.layer;
191         dynarray_delete_at(layer->rects, undo_context->add.index);
192         dynarray_delete_at(layer->colors, undo_context->add.index);
193         dynarray_delete_at(layer->ids, undo_context->add.index);
194         dynarray_delete_at(layer->actions, undo_context->add.index);
195         layer->selection = -1;
196     } break;
197
198     case RECT_UNDO_DELETE: {
199         RectLayer *layer = undo_context->element.layer;
200         dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
201         dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
202         dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
203         dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
204         layer->selection = -1;
205     } break;
206
207     case RECT_UNDO_UPDATE: {
208         RectLayer *layer = undo_context->element.layer;
209         dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
210         dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
211         dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
212         dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
213     } break;
214
215     case RECT_UNDO_SWAP: {
216         RectLayer *layer = undo_context->element.layer;
217         dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
218         dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
219         dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
220         dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
221     } break;
222     }
223 }
224
225 #define RECT_UNDO_PUSH(HISTORY, CONTEXT)                                     \
226     do {                                                                \
227         RectUndoContext context = (CONTEXT);                                \
228         undo_history_push(                                              \
229             HISTORY,                                                    \
230             rect_layer_undo,                                            \
231             &context,                                                   \
232             sizeof(context));                                           \
233     } while(0)
234
235 static int rect_layer_add_rect(RectLayer *layer,
236                                Rect rect,
237                                Color color,
238                                UndoHistory *undo_history)
239 {
240     trace_assert(layer);
241
242     if (dynarray_push(layer->rects, &rect) < 0) {
243         return -1;
244     }
245
246     if (dynarray_push(layer->colors, &color) < 0) {
247         return -1;
248     }
249
250     char id[ENTITY_MAX_ID_SIZE];
251     snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
252              layer->id_name_prefix,
253              layer->id_name_counter++);
254     if (dynarray_push(layer->ids, id)) {
255         return -1;
256     }
257
258     dynarray_push_empty(layer->actions);
259
260     RECT_UNDO_PUSH(
261         undo_history,
262         create_rect_undo_add_context(
263             layer,
264             dynarray_count(layer->rects) - 1));
265
266     return 0;
267 }
268
269 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
270 {
271     trace_assert(layer);
272
273     int n = (int) dynarray_count(layer->rects);
274     Rect *rects = dynarray_data(layer->rects);
275
276     for (int i = n - 1; i >= 0; --i) {
277         if (rect_contains_point(rects[i], position)) {
278             return (int) i;
279         }
280     }
281
282     return -1;
283 }
284
285 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
286                                      UndoHistory *undo_history)
287 {
288     trace_assert(layer);
289     trace_assert(a < dynarray_count(layer->rects));
290     trace_assert(b < dynarray_count(layer->rects));
291
292     dynarray_swap(layer->rects, a, b);
293     dynarray_swap(layer->colors, a, b);
294     dynarray_swap(layer->ids, a, b);
295     dynarray_swap(layer->actions, a, b);
296
297     RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
298 }
299
300 static int rect_layer_delete_rect_at(RectLayer *layer,
301                                      size_t i,
302                                      UndoHistory *undo_history)
303 {
304     trace_assert(layer);
305
306     RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer));
307
308     dynarray_delete_at(layer->rects, i);
309     dynarray_delete_at(layer->colors, i);
310     dynarray_delete_at(layer->ids, i);
311     dynarray_delete_at(layer->actions, i);
312
313     return 0;
314 }
315
316 static int calc_resize_mask(Vec2f point, Rect rect)
317 {
318     int mask = 0;
319     for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
320         if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
321             mask = mask | (1 << side);
322         }
323     }
324     return mask;
325 }
326
327 static int rect_layer_event_idle(RectLayer *layer,
328                                  const SDL_Event *event,
329                                  const Camera *camera,
330                                  UndoHistory *undo_history)
331 {
332     trace_assert(layer);
333     trace_assert(event);
334     trace_assert(camera);
335
336     int color_changed = 0;
337     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
338         return -1;
339     }
340
341     if (color_changed) {
342         if (layer->selection >= 0) {
343             dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
344             layer->state = RECT_LAYER_RECOLOR;
345         }
346         return 0;
347     }
348
349     Rect *rects = dynarray_data(layer->rects);
350
351     switch (event->type) {
352     case SDL_MOUSEBUTTONDOWN: {
353         switch (event->button.button) {
354         case SDL_BUTTON_LEFT: {
355             Vec2f position = camera_map_screen(
356                 camera,
357                 event->button.x,
358                 event->button.y);
359             int rect_at_position =
360                 rect_layer_rect_at(layer, position);
361
362
363             Color *colors = dynarray_data(layer->colors);
364
365             if (layer->selection >= 0 &&
366                 layer->selection == rect_at_position &&
367                 (layer->resize_mask = calc_resize_mask(
368                     vec((float) event->button.x, (float)event->button.y),
369                     camera_rect(camera, rects[layer->selection])))) {
370                 layer->state = RECT_LAYER_RESIZE;
371                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
372             } else if (rect_at_position >= 0) {
373                 layer->selection = rect_at_position;
374                 layer->state = RECT_LAYER_MOVE;
375                 layer->move_anchor = vec_sub(
376                     position,
377                     vec(
378                         rects[layer->selection].x,
379                         rects[layer->selection].y));
380                 layer->color_picker =
381                     create_color_picker_from_rgba(colors[rect_at_position]);
382                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
383             } else {
384                 layer->selection = rect_at_position;
385
386                 if (layer->selection < 0) {
387                     layer->state = RECT_LAYER_CREATE;
388                     layer->create_begin = position;
389                     layer->create_end = position;
390                 }
391             }
392         } break;
393         }
394     } break;
395
396     case SDL_MOUSEMOTION: {
397         int resize_mask = 0;
398         Vec2f position = camera_map_screen(
399             camera,
400             event->button.x,
401             event->button.y);
402         if (layer->selection >= 0 &&
403             layer->selection == rect_layer_rect_at(layer, position) &&
404             (resize_mask = calc_resize_mask(
405                 vec((float) event->button.x, (float)event->button.y),
406                 camera_rect(camera, rects[layer->selection])))) {
407             layer->cursor->style = resize_styles[resize_mask];
408         } else {
409             layer->cursor->style = CURSOR_STYLE_POINTER;
410         }
411     } break;
412
413     case SDL_KEYDOWN: {
414         switch (event->key.keysym.sym) {
415         case SDLK_UP: {
416             if ((event->key.keysym.mod & KMOD_SHIFT)
417                 && (layer->selection >= 0)
418                 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
419                 rect_layer_swap_elements(
420                     layer,
421                     (size_t) layer->selection,
422                     (size_t) layer->selection + 1,
423                     undo_history);
424                 layer->selection++;
425             }
426         } break;
427
428         case SDLK_DOWN: {
429             if ((event->key.keysym.mod & KMOD_SHIFT)
430                 && (layer->selection > 0)
431                 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
432                 rect_layer_swap_elements(
433                     layer,
434                     (size_t) layer->selection,
435                     (size_t) layer->selection - 1,
436                     undo_history);
437                 layer->selection--;
438             }
439         } break;
440
441         case SDLK_DELETE: {
442             if (layer->selection >= 0) {
443                 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
444                 layer->selection = -1;
445             }
446         } break;
447
448         case SDLK_q: {
449             // TODO(#1171): there is no UI indication that we are in the snapping mode
450             layer->snapping_enabled = !layer->snapping_enabled;
451         } break;
452
453         case SDLK_F2: {
454             if (layer->selection >= 0) {
455                 const char *ids = dynarray_data(layer->ids);
456                 Color *colors = dynarray_data(layer->colors);
457
458                 edit_field_restyle(
459                     layer->id_edit_field,
460                     RECT_LAYER_ID_LABEL_SIZE,
461                     color_invert(colors[layer->selection]));
462
463                 layer->state = RECT_LAYER_ID_RENAME;
464                 edit_field_replace(
465                     layer->id_edit_field,
466                     ids + layer->selection * ENTITY_MAX_ID_SIZE);
467                 SDL_StartTextInput();
468             }
469         } break;
470
471         case SDLK_c: {
472             if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
473                 rect_clipboard = 1;
474                 dynarray_copy_to(layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
475                 dynarray_copy_to(layer->colors, &rect_clipboard_color, (size_t)layer->selection);
476             }
477         } break;
478
479         case SDLK_v: {
480             if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
481                 int x, y;
482                 SDL_GetMouseState(&x, &y);
483                 Vec2f position = camera_map_screen(camera, x, y);
484
485                 rect_layer_add_rect(
486                     layer,
487                     rect(position.x, position.y,
488                          rect_clipboard_rect.w, rect_clipboard_rect.h),
489                     rect_clipboard_color,
490                     undo_history);
491             }
492         } break;
493         }
494     } break;
495     }
496
497     return 0;
498 }
499
500 static int rect_layer_event_create(RectLayer *layer,
501                                    const SDL_Event *event,
502                                    const Camera *camera,
503                                    UndoHistory *undo_history)
504 {
505     trace_assert(layer);
506     trace_assert(event);
507     trace_assert(camera);
508
509     switch (event->type) {
510     case SDL_MOUSEBUTTONUP: {
511         switch (event->button.button) {
512         case SDL_BUTTON_LEFT: {
513             const Rect real_rect =
514                 rect_from_points(
515                     layer->create_begin,
516                     layer->create_end);
517             const float area = real_rect.w * real_rect.h;
518
519             if (area >= CREATE_AREA_THRESHOLD) {
520                 rect_layer_add_rect(
521                     layer,
522                     real_rect,
523                     color_picker_rgba(&layer->color_picker),
524                     undo_history);
525             } else {
526                 log_info("The area is too small %f. Such small box won't be created.\n", area);
527             }
528             layer->state = RECT_LAYER_IDLE;
529         } break;
530         }
531     } break;
532
533     case SDL_MOUSEMOTION: {
534         layer->create_end = camera_map_screen(
535             camera,
536             event->motion.x,
537             event->motion.y);
538     } break;
539     }
540     return 0;
541 }
542
543 static
544 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
545 {
546     trace_assert(layer);
547     trace_assert(layer->selection >= 0);
548     trace_assert(a);
549
550     if (!layer->snapping_enabled) return;
551
552     Rect *rects = dynarray_data(layer->rects);
553     size_t rects_size = dynarray_count(layer->rects);
554
555     for (size_t i = 0; i < rects_size; ++i) {
556         if (i == (size_t) layer->selection) continue;
557
558         const Rect b = rects[i];
559
560         if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
561             snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
562         }
563
564         if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
565             snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
566         }
567     }
568 }
569
570 static int rect_layer_event_resize(RectLayer *layer,
571                                    const SDL_Event *event,
572                                    const Camera *camera,
573                                    UndoHistory *undo_history)
574 {
575     trace_assert(layer);
576     trace_assert(event);
577     trace_assert(camera);
578     trace_assert(layer->selection >= 0);
579
580     Rect *rects = dynarray_data(layer->rects);
581
582     float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
583
584     switch (event->type) {
585     case SDL_MOUSEMOTION: {
586         Vec2f position = camera_map_screen(
587             camera,
588             event->button.x,
589             event->button.y);
590
591         switch (layer->resize_mask) {
592         case 1: {               // TOP
593             Rect a = rect(rects[layer->selection].x,
594                           position.y,
595                           rects[layer->selection].w,
596                           rects[layer->selection].h);
597
598             snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
599
600             layer->inter_rect = rect_from_points(
601                 vec(a.x, a.y),
602                 rect_position2(rects[layer->selection]));
603         } break;
604
605         case 2: {               // LEFT
606             Rect a = rect(position.x,
607                           rects[layer->selection].y,
608                           rects[layer->selection].w,
609                           rects[layer->selection].h);
610
611             snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
612
613             layer->inter_rect = rect_from_points(
614                 vec(a.x, a.y),
615                 rect_position2(rects[layer->selection]));
616         } break;
617
618         case 3: {               // TOP,LEFT
619             Rect a = rect(
620                 position.x,
621                 position.y,
622                 rects[layer->selection].w,
623                 rects[layer->selection].h);
624
625             snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
626
627             layer->inter_rect = rect_from_points(
628                 vec(a.x, a.y),
629                 rect_position2(rects[layer->selection]));
630         } break;
631
632         case 4: {               // BOTTOM
633             Rect a = rect(rects[layer->selection].x,
634                           position.y,
635                           rects[layer->selection].w,
636                           rects[layer->selection].h);
637
638             snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
639
640             layer->inter_rect = rect_from_points(
641                 rect_position(rects[layer->selection]),
642                 vec(rects[layer->selection].x + rects[layer->selection].w,
643                     a.y));
644         } break;
645
646         case 6: {               // BOTTOM,LEFT
647             Rect a = rect(
648                 position.x,
649                 position.y,
650                 rects[layer->selection].w,
651                 -rects[layer->selection].h);
652
653             snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
654
655             layer->inter_rect = rect_from_points(
656                 vec(a.x, rects[layer->selection].y),
657                 vec(rects[layer->selection].x + rects[layer->selection].w,
658                     a.y));
659         } break;
660
661         case 8: {               // RIGHT
662             Rect a = rect(position.x,
663                           rects[layer->selection].y,
664                           rects[layer->selection].w,
665                           rects[layer->selection].h);
666
667             snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
668
669             layer->inter_rect = rect_from_points(
670                 rect_position(rects[layer->selection]),
671                 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
672         } break;
673
674         case 9: {               // TOP,RIGHT
675             Rect a = rect(
676                 position.x,
677                 position.y,
678                 -rects[layer->selection].w,
679                 rects[layer->selection].h);
680
681             snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
682
683             layer->inter_rect = rect_from_points(
684                 vec(rects[layer->selection].x, a.y),
685                 vec(a.x,
686                     rects[layer->selection].y + rects[layer->selection].h));
687         } break;
688
689         case 12: {              // BOTTOM,RIGHT
690             Rect a = rect(
691                 position.x,
692                 position.y,
693                 -rects[layer->selection].w,
694                 -rects[layer->selection].h);
695
696             snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
697
698             layer->inter_rect = rect_from_points(
699                 rect_position(rects[layer->selection]),
700                 vec(a.x, a.y));
701         } break;
702         }
703
704     } break;
705
706     case SDL_MOUSEBUTTONUP: {
707         layer->state = RECT_LAYER_IDLE;
708         RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
709         dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
710     } break;
711     }
712
713     return 0;
714 }
715
716 static
717 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
718                                float snapping_threshold)
719 {
720     trace_assert(a);
721     trace_assert(layer);
722     trace_assert(layer->selection >= 0);
723
724     if (!layer->snapping_enabled) return;
725
726     Rect *rects = dynarray_data(layer->rects);
727     size_t rects_size = dynarray_count(layer->rects);
728
729     for (size_t i = 0; i < rects_size; ++i) {
730         if (i == (size_t) layer->selection) continue;
731
732         const Rect b = rects[i];
733
734         if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x,  b.x  + b.w))) {
735             snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
736         }
737
738         if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y,  b.y  + b.h))) {
739             snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
740         }
741     }
742 }
743
744 static int rect_layer_event_move(RectLayer *layer,
745                                  const SDL_Event *event,
746                                  const Camera *camera,
747                                  UndoHistory *undo_history)
748 {
749     trace_assert(layer);
750     trace_assert(event);
751     trace_assert(camera);
752     trace_assert(layer->selection >= 0);
753
754     Rect *rects = dynarray_data(layer->rects);
755
756     switch (event->type) {
757     case SDL_MOUSEMOTION: {
758         const Uint8 *state = SDL_GetKeyboardState(NULL);
759         const Vec2f mouse_pos = vec_sub(
760             camera_map_screen(
761                 camera,
762                 event->button.x,
763                 event->button.y),
764             layer->move_anchor);
765
766         if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
767             layer->inter_rect.x = mouse_pos.x;
768             layer->inter_rect.y = mouse_pos.y;
769         } else {
770             const Vec2f rect_pos = rect_position(rects[layer->selection]);
771
772             const float dx = fabsf(rect_pos.x - mouse_pos.x);
773             const float dy = fabsf(rect_pos.y - mouse_pos.y);
774
775             if (dx > dy) {
776                 layer->inter_rect.x = mouse_pos.x;
777                 layer->inter_rect.y = rect_pos.y;
778             } else {
779                 layer->inter_rect.x = rect_pos.x;
780                 layer->inter_rect.y = mouse_pos.y;
781             }
782         }
783
784         snap_rect_move_if_enabled(layer, &layer->inter_rect,
785                                   SNAPPING_THRESHOLD / camera->scale);
786     } break;
787
788     case SDL_MOUSEBUTTONUP: {
789         layer->state = RECT_LAYER_IDLE;
790
791         float distance = vec_length(
792             vec_sub(rect_position(layer->inter_rect),
793                     rect_position(rects[layer->selection])));
794
795         if (distance > 1e-6) {
796             RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
797             dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
798         }
799     } break;
800     }
801     return 0;
802 }
803
804 static int rect_layer_event_id_rename(RectLayer *layer,
805                                       const SDL_Event *event,
806                                       const Camera *camera,
807                                       UndoHistory *undo_history)
808 {
809     trace_assert(layer);
810     trace_assert(event);
811     trace_assert(camera);
812     trace_assert(layer->selection >= 0);
813
814     switch (event->type) {
815     case SDL_KEYDOWN: {
816         switch (event->key.keysym.sym) {
817         case SDLK_RETURN: {
818             RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
819
820             char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
821             memset(id, 0, ENTITY_MAX_ID_SIZE);
822             memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
823             layer->state = RECT_LAYER_IDLE;
824             SDL_StopTextInput();
825         } break;
826
827         case SDLK_ESCAPE: {
828             layer->state = RECT_LAYER_IDLE;
829             SDL_StopTextInput();
830         } break;
831         }
832     } break;
833     }
834
835     return edit_field_event(layer->id_edit_field, event);
836 }
837
838 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
839 {
840     LayerPtr layer = {
841         .type = LAYER_RECT,
842         .ptr = rect_layer
843     };
844     return layer;
845 }
846
847 RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
848 {
849     trace_assert(cursor);
850
851     Lt *lt = create_lt();
852
853     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
854     if (layer == NULL) {
855         RETURN_LT(lt, NULL);
856     }
857     layer->lt = lt;
858
859     layer->ids = PUSH_LT(
860         lt,
861         create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
862         destroy_dynarray);
863     if (layer->ids == NULL) {
864         RETURN_LT(lt, NULL);
865     }
866
867     layer->rects = PUSH_LT(
868         lt,
869         create_dynarray(sizeof(Rect)),
870         destroy_dynarray);
871     if (layer->rects == NULL) {
872         RETURN_LT(lt, NULL);
873     }
874
875     layer->colors = PUSH_LT(
876         lt,
877         create_dynarray(sizeof(Color)),
878         destroy_dynarray);
879     if (layer->colors == NULL) {
880         RETURN_LT(lt, NULL);
881     }
882
883     layer->actions = PUSH_LT(
884         lt,
885         create_dynarray(sizeof(Action)),
886         destroy_dynarray);
887     if (layer->actions == NULL) {
888         RETURN_LT(lt, NULL);
889     }
890
891     layer->id_edit_field = PUSH_LT(
892         lt,
893         create_edit_field(
894             RECT_LAYER_ID_LABEL_SIZE,
895             COLOR_BLACK),
896         destroy_edit_field);
897     if (layer->id_edit_field == NULL) {
898         RETURN_LT(lt, NULL);
899     }
900
901     layer->grid =
902         PUSH_LT(
903             lt,
904             nth_calloc(
905                 1,
906                 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
907             free);
908     if (layer->grid == NULL) {
909         RETURN_LT(lt, NULL);
910     }
911     layer->grid->rows = RECT_LAYER_GRID_ROWS;
912     layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
913     grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
914
915     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
916     layer->selection = -1;
917     layer->id_name_prefix = id_name_prefix;
918     layer->cursor = cursor;
919
920     return layer;
921 }
922
923 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream,
924                                               const char *id_name_prefix,
925                                               Cursor *cursor)
926 {
927     trace_assert(line_stream);
928
929     RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
930     if (layer == NULL) {
931         return NULL;
932     }
933
934     const char *line = line_stream_next(line_stream);
935     if (line == NULL) {
936         RETURN_LT(layer->lt, NULL);
937     }
938
939     size_t count = 0;
940     if (sscanf(line, "%zu", &count) < 0) {
941         RETURN_LT(layer->lt, NULL);
942     }
943
944     for (size_t i = 0; i < count; ++i) {
945         line = line_stream_next(line_stream);
946         if (line == NULL) {
947             RETURN_LT(layer->lt, NULL);
948         }
949
950         char hex[7];
951         Rect rect;
952         char id[ENTITY_MAX_ID_SIZE];
953
954         int n = 0;
955         if (sscanf(line,
956                    "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
957                    id,
958                    &rect.x, &rect.y,
959                    &rect.w, &rect.h,
960                    hex, &n) <= 0) {
961             log_fail("%s\n", strerror(errno));
962             RETURN_LT(layer->lt, NULL);
963         }
964         line += n;
965
966         Color color = hexstr(hex);
967         dynarray_push(layer->rects, &rect);
968         dynarray_push(layer->ids, id);
969         dynarray_push(layer->colors, &color);
970
971         Action action = {
972             .type = ACTION_NONE,
973             .entity_id = {0}
974         };
975
976         if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
977             line += n;
978             switch (action.type) {
979             case ACTION_NONE: break;
980
981             case ACTION_TOGGLE_GOAL:
982             case ACTION_HIDE_LABEL: {
983                 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
984                     log_fail("%s\n", strerror(errno));
985                     RETURN_LT(layer->lt, NULL);
986                 }
987             } break;
988
989             case ACTION_N: break;
990             }
991         }
992
993         dynarray_push(layer->actions, &action);
994     }
995
996     return layer;
997 }
998
999 void destroy_rect_layer(RectLayer *layer)
1000 {
1001     trace_assert(layer);
1002     RETURN_LT0(layer->lt);
1003 }
1004
1005 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
1006 {
1007     trace_assert(layer);
1008     trace_assert(camera);
1009
1010     const size_t n = dynarray_count(layer->rects);
1011     Rect *rects = dynarray_data(layer->rects);
1012     Color *colors = dynarray_data(layer->colors);
1013     const char *ids = dynarray_data(layer->ids);
1014
1015     // The Rectangles
1016     for (size_t i = 0; i < n; ++i) {
1017         Rect rect = rects[i];
1018         Color color = colors[i];
1019
1020         if (layer->selection == (int) i) {
1021             if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1022                 rect = layer->inter_rect;
1023             }
1024
1025             if (layer->state == RECT_LAYER_RECOLOR) {
1026                 color = layer->inter_color;
1027             }
1028         }
1029
1030         // Main Rectangle
1031         if (camera_fill_rect(
1032                 camera,
1033                 rect,
1034                 color_scale(
1035                     color,
1036                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
1037             return -1;
1038         }
1039     }
1040
1041     // Selection Overlay
1042     if (active && layer->selection >= 0) {
1043         Rect rect = rects[layer->selection];
1044         Color color = colors[layer->selection];
1045
1046         if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1047             rect = layer->inter_rect;
1048         }
1049
1050         if (layer->state == RECT_LAYER_RECOLOR) {
1051             color = layer->inter_color;
1052         }
1053
1054         const Rect overlay_rect =
1055             rect_pad(
1056                 camera_rect(camera, rect),
1057                 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1058         const Color overlay_color = color_invert(color);
1059
1060         // Selection
1061         if (camera_draw_thicc_rect_screen(
1062                 camera,
1063                 overlay_rect,
1064                 overlay_color,
1065                 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1066             return -1;
1067         }
1068
1069         const Vec2f rect_id_pos = vec_sub(
1070             rect_position(rect),
1071             vec_mult(
1072                 RECT_LAYER_ID_LABEL_SIZE,
1073                 vec(0.0f, FONT_CHAR_HEIGHT)));
1074
1075         // Rectangle Id
1076         if (layer->state == RECT_LAYER_ID_RENAME) {
1077             // ID renaming Edit Field
1078             if (edit_field_render_world(
1079                     layer->id_edit_field,
1080                     camera,
1081                     rect_id_pos) < 0) {
1082                 return -1;
1083             }
1084         } else {
1085             // Id text
1086             if (camera_render_text(
1087                     camera,
1088                     ids + layer->selection * ENTITY_MAX_ID_SIZE,
1089                     RECT_LAYER_ID_LABEL_SIZE,
1090                     color_invert(color),
1091                     rect_id_pos) < 0) {
1092                 return -1;
1093             }
1094         }
1095     }
1096
1097     // Proto Rectangle
1098     const Color color = color_picker_rgba(&layer->color_picker);
1099     if (layer->state == RECT_LAYER_CREATE) {
1100         if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1101             return -1;
1102         }
1103     }
1104
1105     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1106         return -1;
1107     }
1108
1109     return 0;
1110 }
1111
1112 static
1113 int rect_layer_event_recolor(RectLayer *layer,
1114                              const SDL_Event *event,
1115                              const Camera *camera,
1116                              UndoHistory *undo_history)
1117 {
1118     trace_assert(layer);
1119     trace_assert(event);
1120     trace_assert(camera);
1121     trace_assert(undo_history);
1122     trace_assert(layer->selection >= 0);
1123
1124     int color_changed = 0;
1125     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1126         return -1;
1127     }
1128
1129     if (color_changed) {
1130         layer->inter_color = color_picker_rgba(&layer->color_picker);
1131
1132         if (!color_picker_drag(&layer->color_picker)) {
1133             RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
1134             dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
1135             layer->state = RECT_LAYER_IDLE;
1136         }
1137     }
1138
1139     return 0;
1140 }
1141
1142 int rect_layer_event(RectLayer *layer,
1143                      const SDL_Event *event,
1144                      const Camera *camera,
1145                      UndoHistory *undo_history)
1146 {
1147     trace_assert(layer);
1148     trace_assert(event);
1149     trace_assert(undo_history);
1150
1151     switch (event->type) {
1152     case SDL_WINDOWEVENT: {
1153         switch (event->window.event) {
1154         case SDL_WINDOWEVENT_SIZE_CHANGED: {
1155             grid_relayout(layer->grid, rect(0.0f, 0.0f,
1156                                             (float) event->window.data1,
1157                                             (float) event->window.data2));
1158         } break;
1159         }
1160     } break;
1161     }
1162
1163     switch (layer->state) {
1164     case RECT_LAYER_IDLE:
1165         return rect_layer_event_idle(layer, event, camera, undo_history);
1166
1167     case RECT_LAYER_CREATE:
1168         return rect_layer_event_create(layer, event, camera, undo_history);
1169
1170     case RECT_LAYER_RESIZE:
1171         return rect_layer_event_resize(layer, event, camera, undo_history);
1172
1173     case RECT_LAYER_MOVE:
1174         return rect_layer_event_move(layer, event, camera, undo_history);
1175
1176     case RECT_LAYER_ID_RENAME:
1177         return rect_layer_event_id_rename(layer, event, camera, undo_history);
1178
1179     case RECT_LAYER_RECOLOR:
1180         return rect_layer_event_recolor(layer, event, camera, undo_history);
1181     }
1182
1183
1184     return 0;
1185 }
1186
1187 size_t rect_layer_count(const RectLayer *layer)
1188 {
1189     return dynarray_count(layer->rects);
1190 }
1191
1192 const Rect *rect_layer_rects(const RectLayer *layer)
1193 {
1194     return dynarray_data(layer->rects);
1195 }
1196
1197 const Color *rect_layer_colors(const RectLayer *layer)
1198 {
1199     return dynarray_data(layer->colors);
1200 }
1201
1202 const char *rect_layer_ids(const RectLayer *layer)
1203 {
1204     return dynarray_data(layer->ids);
1205 }
1206
1207 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1208 {
1209     trace_assert(layer);
1210     trace_assert(filedump);
1211
1212     size_t n = dynarray_count(layer->ids);
1213     char *ids = dynarray_data(layer->ids);
1214     Rect *rects = dynarray_data(layer->rects);
1215     Color *colors = dynarray_data(layer->colors);
1216     Action *actions = dynarray_data(layer->actions);
1217
1218     fprintf(filedump, "%zd\n", n);
1219     for (size_t i = 0; i < n; ++i) {
1220         fprintf(filedump, "%s %f %f %f %f ",
1221                 ids + ENTITY_MAX_ID_SIZE * i,
1222                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1223         color_hex_to_stream(colors[i], filedump);
1224
1225         switch (actions[i].type) {
1226         case ACTION_NONE: {} break;
1227
1228         case ACTION_TOGGLE_GOAL:
1229         case ACTION_HIDE_LABEL: {
1230             fprintf(filedump, " %d %.*s",
1231                     (int)actions[i].type,
1232                     ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1233         } break;
1234         case ACTION_N: break;
1235         }
1236
1237         fprintf(filedump, "\n");
1238     }
1239
1240     return 0;
1241 }
1242
1243 const Action *rect_layer_actions(const RectLayer *layer)
1244 {
1245     return dynarray_data(layer->actions);
1246 }