]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
da8da20c0bc9b22b719a3d68e87b0519daa0e040
[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 clipboard = 0;
29 static Rect clipboard_rect;
30 static Color 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     // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
52     // TODO(#1129): different cursor image in resize mode
53     RECT_LAYER_RESIZE,
54     RECT_LAYER_MOVE,
55     RECT_LAYER_ID_RENAME,
56     RECT_LAYER_RECOLOR
57 } RectLayerState;
58
59 struct RectLayer {
60     Lt *lt;
61     RectLayerState state;
62     int resize_mask;
63     Dynarray *ids;
64     Dynarray *rects;
65     Dynarray *colors;
66     Dynarray *actions;
67     ColorPicker color_picker;
68     ActionPicker action_picker;
69     Vec2f create_begin;
70     Vec2f create_end;
71     int selection;
72     Vec2f move_anchor;          // The mouse offset from the left-top
73                                 // corner of the rect during moving it
74     Edit_field *id_edit_field;
75     Color inter_color;
76     Rect inter_rect;
77     int id_name_counter;
78     const char *id_name_prefix;
79     Grid *grid;
80     Cursor *cursor;
81 };
82
83 typedef enum {
84     UNDO_ADD,
85     UNDO_DELETE,
86     UNDO_UPDATE,
87     UNDO_SWAP
88 } UndoType;
89
90 // Delete, Update
91 typedef struct {
92     UndoType 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     UndoType type;
104     RectLayer *layer;
105     size_t index;
106 } UndoAddContext;
107
108 // Swap
109 typedef struct {
110     UndoType type;
111     RectLayer *layer;
112     size_t index1;
113     size_t index2;
114 } UndoSwapContext;
115
116 typedef union {
117     UndoType type;
118     UndoAddContext add;
119     UndoElementContext element;
120     UndoSwapContext swap;
121 } UndoContext;
122
123 static
124 UndoContext create_undo_add_context(RectLayer *layer, size_t index)
125 {
126     trace_assert(layer);
127     trace_assert(index < dynarray_count(layer->rects));
128
129     UndoContext undo_context;
130     undo_context.add.type = UNDO_ADD;
131     undo_context.add.layer = layer;
132     undo_context.add.index = index;
133     return undo_context;
134 }
135
136 static
137 UndoContext create_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     UndoContext 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 UndoContext create_undo_update_context(RectLayer *rect_layer)
155 {
156     UndoContext undo_context = create_undo_element_context(rect_layer);
157     undo_context.type = UNDO_UPDATE;
158     return undo_context;
159 }
160
161 static
162 UndoContext create_undo_delete_context(RectLayer *rect_layer)
163 {
164     UndoContext undo_context = create_undo_element_context(rect_layer);
165     undo_context.type = UNDO_DELETE;
166     return undo_context;
167 }
168
169 static
170 UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
171 {
172     UndoContext undo_context;
173     undo_context.swap.type = 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(UndoContext) == context_size);
185
186     UndoContext *undo_context = context;
187
188     switch (undo_context->type) {
189     case 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 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 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 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 UNDO_PUSH(HISTORY, CONTEXT)                                     \
226     do {                                                                \
227         UndoContext 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     UNDO_PUSH(
261         undo_history,
262         create_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     UNDO_PUSH(undo_history, create_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     UNDO_PUSH(undo_history, create_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             Color *colors = dynarray_data(layer->colors);
363
364             if (layer->selection >= 0 &&
365                 layer->selection == rect_at_position &&
366                 (layer->resize_mask = calc_resize_mask(
367                     vec((float) event->button.x, (float)event->button.y),
368                     camera_rect(camera, rects[layer->selection])))) {
369                 layer->state = RECT_LAYER_RESIZE;
370                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
371             } else if (rect_at_position >= 0) {
372                 layer->selection = rect_at_position;
373                 layer->state = RECT_LAYER_MOVE;
374                 layer->move_anchor =
375                     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
383                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
384             } else {
385                 layer->selection = rect_at_position;
386
387                 if (layer->selection < 0) {
388                     layer->state = RECT_LAYER_CREATE;
389                     layer->create_begin = position;
390                     layer->create_end = position;
391                 }
392             }
393         } break;
394         }
395     } break;
396
397     case SDL_MOUSEMOTION: {
398         int resize_mask = 0;
399         Vec2f position = camera_map_screen(
400             camera,
401             event->button.x,
402             event->button.y);
403         if (layer->selection >= 0 &&
404             layer->selection == rect_layer_rect_at(layer, position) &&
405             (resize_mask = calc_resize_mask(
406                 vec((float) event->button.x, (float)event->button.y),
407                 camera_rect(camera, rects[layer->selection])))) {
408             layer->cursor->style = resize_styles[resize_mask];
409         } else {
410             layer->cursor->style = CURSOR_STYLE_POINTER;
411         }
412     } break;
413
414     case SDL_KEYDOWN: {
415         switch (event->key.keysym.sym) {
416         case SDLK_UP: {
417             if ((event->key.keysym.mod & KMOD_SHIFT)
418                 && (layer->selection >= 0)
419                 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
420                 rect_layer_swap_elements(
421                     layer,
422                     (size_t) layer->selection,
423                     (size_t) layer->selection + 1,
424                     undo_history);
425                 layer->selection++;
426             }
427         } break;
428
429         case SDLK_DOWN: {
430             if ((event->key.keysym.mod & KMOD_SHIFT)
431                 && (layer->selection > 0)
432                 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
433                 rect_layer_swap_elements(
434                     layer,
435                     (size_t) layer->selection,
436                     (size_t) layer->selection - 1,
437                     undo_history);
438                 layer->selection--;
439             }
440         } break;
441
442         case SDLK_DELETE: {
443             if (layer->selection >= 0) {
444                 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
445                 layer->selection = -1;
446             }
447         } break;
448
449         case SDLK_F2: {
450             if (layer->selection >= 0) {
451                 const char *ids = dynarray_data(layer->ids);
452                 Color *colors = dynarray_data(layer->colors);
453
454                 edit_field_restyle(
455                     layer->id_edit_field,
456                     RECT_LAYER_ID_LABEL_SIZE,
457                     color_invert(colors[layer->selection]));
458
459                 layer->state = RECT_LAYER_ID_RENAME;
460                 edit_field_replace(
461                     layer->id_edit_field,
462                     ids + layer->selection * ENTITY_MAX_ID_SIZE);
463                 SDL_StartTextInput();
464             }
465         } break;
466
467         case SDLK_c: {
468             if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
469                 clipboard = 1;
470                 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
471                 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
472             }
473         } break;
474
475         case SDLK_v: {
476             if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
477                 int x, y;
478                 SDL_GetMouseState(&x, &y);
479                 Vec2f position = camera_map_screen(camera, x, y);
480
481                 rect_layer_add_rect(
482                     layer,
483                     rect(position.x, position.y,
484                          clipboard_rect.w, clipboard_rect.h),
485                     clipboard_color,
486                     undo_history);
487             }
488         } break;
489         }
490     } break;
491     }
492
493     return 0;
494 }
495
496 static int rect_layer_event_create(RectLayer *layer,
497                                    const SDL_Event *event,
498                                    const Camera *camera,
499                                    UndoHistory *undo_history)
500 {
501     trace_assert(layer);
502     trace_assert(event);
503     trace_assert(camera);
504
505     switch (event->type) {
506     case SDL_MOUSEBUTTONUP: {
507         switch (event->button.button) {
508         case SDL_BUTTON_LEFT: {
509             const Rect real_rect =
510                 rect_from_points(
511                     layer->create_begin,
512                     layer->create_end);
513             const float area = real_rect.w * real_rect.h;
514
515             if (area >= CREATE_AREA_THRESHOLD) {
516                 rect_layer_add_rect(
517                     layer,
518                     real_rect,
519                     color_picker_rgba(&layer->color_picker),
520                     undo_history);
521             } else {
522                 log_info("The area is too small %f. Such small box won't be created.\n", area);
523             }
524             layer->state = RECT_LAYER_IDLE;
525         } break;
526         }
527     } break;
528
529     case SDL_MOUSEMOTION: {
530         layer->create_end = camera_map_screen(
531             camera,
532             event->motion.x,
533             event->motion.y);
534     } break;
535     }
536     return 0;
537 }
538
539 static int rect_layer_event_resize(RectLayer *layer,
540                                    const SDL_Event *event,
541                                    const Camera *camera,
542                                    UndoHistory *undo_history)
543 {
544     trace_assert(layer);
545     trace_assert(event);
546     trace_assert(camera);
547     trace_assert(layer->selection >= 0);
548
549     Rect *rects = dynarray_data(layer->rects);
550
551     switch (event->type) {
552     case SDL_MOUSEMOTION: {
553         Vec2f position = camera_map_screen(
554             camera,
555             event->button.x,
556             event->button.y);
557
558         switch (layer->resize_mask) {
559         case 1: {               // TOP
560             layer->inter_rect = rect_from_points(
561                 vec(rects[layer->selection].x, position.y),
562                 rect_position2(rects[layer->selection]));
563         } break;
564
565         case 2: {               // LEFT
566             layer->inter_rect = rect_from_points(
567                 vec(position.x, rects[layer->selection].y),
568                 rect_position2(rects[layer->selection]));
569         } break;
570
571         case 3: {               // TOP,LEFT
572             layer->inter_rect = rect_from_points(
573                 position,
574                 rect_position2(rects[layer->selection]));
575         } break;
576
577         case 4: {               // BOTTOM
578             layer->inter_rect = rect_from_points(
579                 rect_position(rects[layer->selection]),
580                 vec(rects[layer->selection].x + rects[layer->selection].w,
581                     position.y));
582         } break;
583
584         case 6: {               // BOTTOM,LEFT
585             layer->inter_rect = rect_from_points(
586                 vec(position.x, rects[layer->selection].y),
587                 vec(rects[layer->selection].x + rects[layer->selection].w,
588                     position.y));
589         } break;
590
591         case 8: {               // RIGHT
592             layer->inter_rect = rect_from_points(
593                 rect_position(rects[layer->selection]),
594                 vec(position.x,
595                     rects[layer->selection].y + rects[layer->selection].h));
596         } break;
597
598         case 9: {               // TOP,RIGHT
599             layer->inter_rect = rect_from_points(
600                 vec(rects[layer->selection].x, position.y),
601                 vec(position.x,
602                     rects[layer->selection].y + rects[layer->selection].h));
603         } break;
604
605         case 12: {              // BOTTOM,RIGHT
606             layer->inter_rect = rect_from_points(
607                 rect_position(rects[layer->selection]),
608                 position);
609         } break;
610         }
611     } break;
612
613     case SDL_MOUSEBUTTONUP: {
614         layer->state = RECT_LAYER_IDLE;
615         UNDO_PUSH(undo_history, create_undo_update_context(layer));
616         dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
617     } break;
618     }
619
620     return 0;
621 }
622
623 static
624 int segment_overlap(Vec2f a, Vec2f b)
625 {
626     trace_assert(a.x <= a.y);
627     trace_assert(b.x <= b.y);
628     return a.y >= b.x && b.y >= a.x;
629 }
630
631 static
632 void snap_rect(Rect *a, Rect b)
633 {
634     trace_assert(a);
635
636 #define SNAPPING_THRESHOLD 10.0f
637
638     for (Rect_side a_side = 0; a_side < RECT_SIDE_N; ++a_side) {
639         for (Rect_side b_side = 0; b_side < RECT_SIDE_N; ++b_side) {
640             if (a_side == RECT_SIDE_BOTTOM &&
641                 b_side == RECT_SIDE_TOP &&
642                 segment_overlap(
643                     vec(a->x, a->x + a->w),
644                     vec(b.x,  b.x  + b.w)) &&
645                 fabsf((a->y + a->h) - b.y) < SNAPPING_THRESHOLD) {
646                 a->y = b.y - a->h;
647             } else if (a_side == RECT_SIDE_TOP &&
648                        b_side == RECT_SIDE_TOP &&
649                        segment_overlap(
650                            vec(a->x, a->x + a->w),
651                            vec(b.x,  b.x  + b.w)) &&
652                        fabsf(a->y - b.y) < SNAPPING_THRESHOLD) {
653                 a->y = b.y;
654             } else if (a_side == RECT_SIDE_LEFT &&
655                        b_side == RECT_SIDE_RIGHT &&
656                        segment_overlap(
657                            vec(a->y, a->y + a->h),
658                            vec(b.y,  b.y  + b.h)) &&
659                        fabsf(a->x - (b.x + b.w)) < SNAPPING_THRESHOLD) {
660                 a->x = b.x + b.w;
661             } else if (a_side == RECT_SIDE_RIGHT &&
662                        b_side == RECT_SIDE_RIGHT &&
663                        segment_overlap(
664                            vec(a->y, a->y + a->h),
665                            vec(b.y,  b.y  + b.h)) &&
666                        fabsf((a->x + a->w) - (b.x + b.w)) < SNAPPING_THRESHOLD) {
667                 a->x = b.x + b.w - a->w;
668             } else if (a_side == RECT_SIDE_TOP &&
669                        b_side == RECT_SIDE_BOTTOM &&
670                        segment_overlap(
671                            vec(a->x, a->x + a->w),
672                            vec(b.x,  b.x  + b.w)) &&
673                        fabsf(a->y - (b.y + b.h)) < SNAPPING_THRESHOLD) {
674                 a->y = b.y + b.h;
675             } else if (a_side == RECT_SIDE_BOTTOM &&
676                        b_side == RECT_SIDE_BOTTOM &&
677                        segment_overlap(
678                            vec(a->x, a->x + a->w),
679                            vec(b.x,  b.x  + b.w)) &&
680                        fabsf((a->y + a->h) - (b.y + b.h)) < SNAPPING_THRESHOLD) {
681                 a->y = b.y + b.h - a->h;
682             } else if (a_side == RECT_SIDE_LEFT &&
683                        b_side == RECT_SIDE_RIGHT &&
684                        segment_overlap(
685                            vec(a->y, a->y + a->h),
686                            vec(b.y,  b.y  + b.h)) &&
687                        fabs((a->x + a->w) - b.x) < SNAPPING_THRESHOLD) {
688                 a->x = b.x - a->w;
689             } else if (a_side == RECT_SIDE_LEFT &&
690                        b_side == RECT_SIDE_LEFT &&
691                        segment_overlap(
692                            vec(a->y, a->y + a->h),
693                            vec(b.y,  b.y  + b.h)) &&
694                        fabs(a->x - b.x) < SNAPPING_THRESHOLD) {
695                 a->x = b.x;
696             }
697         }
698     }
699 }
700
701 static
702 void snap_rects(size_t ignore_index, Rect *rect,
703                 Rect *rects, size_t rects_size)
704 {
705     trace_assert(rects);
706     trace_assert(rect);
707
708     for (size_t i = 0; i < rects_size; ++i) {
709         if (i == ignore_index) continue;
710         snap_rect(rect, rects[i]);
711     }
712 }
713
714 static int rect_layer_event_move(RectLayer *layer,
715                                  const SDL_Event *event,
716                                  const Camera *camera,
717                                  UndoHistory *undo_history)
718 {
719     trace_assert(layer);
720     trace_assert(event);
721     trace_assert(camera);
722     trace_assert(layer->selection >= 0);
723
724     Rect *rects = dynarray_data(layer->rects);
725
726     switch (event->type) {
727     case SDL_MOUSEMOTION: {
728         const Uint8 *state = SDL_GetKeyboardState(NULL);
729         const Vec2f mouse_pos = vec_sub(
730             camera_map_screen(
731                 camera,
732                 event->button.x,
733                 event->button.y),
734             layer->move_anchor);
735
736         if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
737             layer->inter_rect.x = mouse_pos.x;
738             layer->inter_rect.y = mouse_pos.y;
739         } else {
740             const Vec2f rect_pos = rect_position(rects[layer->selection]);
741
742             const float dx = fabsf(rect_pos.x - mouse_pos.x);
743             const float dy = fabsf(rect_pos.y - mouse_pos.y);
744
745             if (dx > dy) {
746                 layer->inter_rect.x = mouse_pos.x;
747                 layer->inter_rect.y = rect_pos.y;
748             } else {
749                 layer->inter_rect.x = rect_pos.x;
750                 layer->inter_rect.y = mouse_pos.y;
751             }
752         }
753
754         // TODO: Rect Snapping in Level Editor should be optional
755         // TODO: Resize mode of Rect Layer does not support Snapping
756         snap_rects((size_t) layer->selection, &layer->inter_rect,
757                    rects, dynarray_count(layer->rects));
758     } break;
759
760     case SDL_MOUSEBUTTONUP: {
761         layer->state = RECT_LAYER_IDLE;
762
763         float distance = vec_length(
764             vec_sub(rect_position(layer->inter_rect),
765                     rect_position(rects[layer->selection])));
766
767         if (distance > 1e-6) {
768             UNDO_PUSH(undo_history, create_undo_update_context(layer));
769             dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
770         }
771     } break;
772     }
773     return 0;
774 }
775
776 static int rect_layer_event_id_rename(RectLayer *layer,
777                                       const SDL_Event *event,
778                                       const Camera *camera,
779                                       UndoHistory *undo_history)
780 {
781     trace_assert(layer);
782     trace_assert(event);
783     trace_assert(camera);
784     trace_assert(layer->selection >= 0);
785
786     switch (event->type) {
787     case SDL_KEYDOWN: {
788         switch (event->key.keysym.sym) {
789         case SDLK_RETURN: {
790             UNDO_PUSH(undo_history, create_undo_update_context(layer));
791
792             char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
793             memset(id, 0, ENTITY_MAX_ID_SIZE);
794             memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
795             layer->state = RECT_LAYER_IDLE;
796             SDL_StopTextInput();
797         } break;
798
799         case SDLK_ESCAPE: {
800             layer->state = RECT_LAYER_IDLE;
801             SDL_StopTextInput();
802         } break;
803         }
804     } break;
805     }
806
807     return edit_field_event(layer->id_edit_field, event);
808 }
809
810 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
811 {
812     LayerPtr layer = {
813         .type = LAYER_RECT,
814         .ptr = rect_layer
815     };
816     return layer;
817 }
818
819 RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
820 {
821     trace_assert(cursor);
822
823     Lt *lt = create_lt();
824
825     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
826     if (layer == NULL) {
827         RETURN_LT(lt, NULL);
828     }
829     layer->lt = lt;
830
831     layer->ids = PUSH_LT(
832         lt,
833         create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
834         destroy_dynarray);
835     if (layer->ids == NULL) {
836         RETURN_LT(lt, NULL);
837     }
838
839     layer->rects = PUSH_LT(
840         lt,
841         create_dynarray(sizeof(Rect)),
842         destroy_dynarray);
843     if (layer->rects == NULL) {
844         RETURN_LT(lt, NULL);
845     }
846
847     layer->colors = PUSH_LT(
848         lt,
849         create_dynarray(sizeof(Color)),
850         destroy_dynarray);
851     if (layer->colors == NULL) {
852         RETURN_LT(lt, NULL);
853     }
854
855     layer->actions = PUSH_LT(
856         lt,
857         create_dynarray(sizeof(Action)),
858         destroy_dynarray);
859     if (layer->actions == NULL) {
860         RETURN_LT(lt, NULL);
861     }
862
863     layer->id_edit_field = PUSH_LT(
864         lt,
865         create_edit_field(
866             RECT_LAYER_ID_LABEL_SIZE,
867             COLOR_BLACK),
868         destroy_edit_field);
869     if (layer->id_edit_field == NULL) {
870         RETURN_LT(lt, NULL);
871     }
872
873     layer->grid =
874         PUSH_LT(
875             lt,
876             nth_calloc(
877                 1,
878                 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
879             free);
880     if (layer->grid == NULL) {
881         RETURN_LT(lt, NULL);
882     }
883     layer->grid->rows = RECT_LAYER_GRID_ROWS;
884     layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
885     grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
886
887     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
888     layer->selection = -1;
889     layer->id_name_prefix = id_name_prefix;
890     layer->cursor = cursor;
891
892     return layer;
893 }
894
895 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream,
896                                               const char *id_name_prefix,
897                                               Cursor *cursor)
898 {
899     trace_assert(line_stream);
900
901     RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
902     if (layer == NULL) {
903         return NULL;
904     }
905
906     const char *line = line_stream_next(line_stream);
907     if (line == NULL) {
908         RETURN_LT(layer->lt, NULL);
909     }
910
911     size_t count = 0;
912     if (sscanf(line, "%zu", &count) < 0) {
913         RETURN_LT(layer->lt, NULL);
914     }
915
916     for (size_t i = 0; i < count; ++i) {
917         line = line_stream_next(line_stream);
918         if (line == NULL) {
919             RETURN_LT(layer->lt, NULL);
920         }
921
922         char hex[7];
923         Rect rect;
924         char id[ENTITY_MAX_ID_SIZE];
925
926         int n = 0;
927         if (sscanf(line,
928                    "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
929                    id,
930                    &rect.x, &rect.y,
931                    &rect.w, &rect.h,
932                    hex, &n) <= 0) {
933             log_fail("%s\n", strerror(errno));
934             RETURN_LT(layer->lt, NULL);
935         }
936         line += n;
937
938         Color color = hexstr(hex);
939         dynarray_push(layer->rects, &rect);
940         dynarray_push(layer->ids, id);
941         dynarray_push(layer->colors, &color);
942
943         Action action = {
944             .type = ACTION_NONE,
945             .entity_id = {0}
946         };
947
948         if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
949             line += n;
950             switch (action.type) {
951             case ACTION_NONE: break;
952
953             case ACTION_TOGGLE_GOAL:
954             case ACTION_HIDE_LABEL: {
955                 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
956                     log_fail("%s\n", strerror(errno));
957                     RETURN_LT(layer->lt, NULL);
958                 }
959             } break;
960
961             case ACTION_N: break;
962             }
963         }
964
965         dynarray_push(layer->actions, &action);
966     }
967
968     return layer;
969 }
970
971 void destroy_rect_layer(RectLayer *layer)
972 {
973     trace_assert(layer);
974     RETURN_LT0(layer->lt);
975 }
976
977 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
978 {
979     trace_assert(layer);
980     trace_assert(camera);
981
982     const size_t n = dynarray_count(layer->rects);
983     Rect *rects = dynarray_data(layer->rects);
984     Color *colors = dynarray_data(layer->colors);
985     const char *ids = dynarray_data(layer->ids);
986
987     // The Rectangles
988     for (size_t i = 0; i < n; ++i) {
989         Rect rect = rects[i];
990         Color color = colors[i];
991
992         if (layer->selection == (int) i) {
993             if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
994                 rect = layer->inter_rect;
995             }
996
997             if (layer->state == RECT_LAYER_RECOLOR) {
998                 color = layer->inter_color;
999             }
1000         }
1001
1002         // Main Rectangle
1003         if (camera_fill_rect(
1004                 camera,
1005                 rect,
1006                 color_scale(
1007                     color,
1008                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
1009             return -1;
1010         }
1011     }
1012
1013     // Selection Overlay
1014     if (active && layer->selection >= 0) {
1015         Rect rect = rects[layer->selection];
1016         Color color = colors[layer->selection];
1017
1018         if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1019             rect = layer->inter_rect;
1020         }
1021
1022         if (layer->state == RECT_LAYER_RECOLOR) {
1023             color = layer->inter_color;
1024         }
1025
1026         const Rect overlay_rect =
1027             rect_pad(
1028                 camera_rect(camera, rect),
1029                 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1030         const Color overlay_color = color_invert(color);
1031
1032         // Selection
1033         if (camera_draw_thicc_rect_screen(
1034                 camera,
1035                 overlay_rect,
1036                 overlay_color,
1037                 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1038             return -1;
1039         }
1040
1041         const Vec2f rect_id_pos = vec_sub(
1042             rect_position(rect),
1043             vec_mult(
1044                 RECT_LAYER_ID_LABEL_SIZE,
1045                 vec(0.0f, FONT_CHAR_HEIGHT)));
1046
1047         // Rectangle Id
1048         if (layer->state == RECT_LAYER_ID_RENAME) {
1049             // ID renaming Edit Field
1050             if (edit_field_render_world(
1051                     layer->id_edit_field,
1052                     camera,
1053                     rect_id_pos) < 0) {
1054                 return -1;
1055             }
1056         } else {
1057             // Id text
1058             if (camera_render_text(
1059                     camera,
1060                     ids + layer->selection * ENTITY_MAX_ID_SIZE,
1061                     RECT_LAYER_ID_LABEL_SIZE,
1062                     color_invert(color),
1063                     rect_id_pos) < 0) {
1064                 return -1;
1065             }
1066         }
1067     }
1068
1069     // Proto Rectangle
1070     const Color color = color_picker_rgba(&layer->color_picker);
1071     if (layer->state == RECT_LAYER_CREATE) {
1072         if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1073             return -1;
1074         }
1075     }
1076
1077     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1078         return -1;
1079     }
1080
1081     return 0;
1082 }
1083
1084 static
1085 int rect_layer_event_recolor(RectLayer *layer,
1086                              const SDL_Event *event,
1087                              const Camera *camera,
1088                              UndoHistory *undo_history)
1089 {
1090     trace_assert(layer);
1091     trace_assert(event);
1092     trace_assert(camera);
1093     trace_assert(undo_history);
1094     trace_assert(layer->selection >= 0);
1095
1096     int color_changed = 0;
1097     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1098         return -1;
1099     }
1100
1101     if (color_changed) {
1102         layer->inter_color = color_picker_rgba(&layer->color_picker);
1103
1104         if (!color_picker_drag(&layer->color_picker)) {
1105             UNDO_PUSH(undo_history, create_undo_update_context(layer));
1106             dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
1107             layer->state = RECT_LAYER_IDLE;
1108         }
1109     }
1110
1111     return 0;
1112 }
1113
1114 int rect_layer_event(RectLayer *layer,
1115                      const SDL_Event *event,
1116                      const Camera *camera,
1117                      UndoHistory *undo_history)
1118 {
1119     trace_assert(layer);
1120     trace_assert(event);
1121     trace_assert(undo_history);
1122
1123     switch (event->type) {
1124     case SDL_WINDOWEVENT: {
1125         switch (event->window.event) {
1126         case SDL_WINDOWEVENT_RESIZED: {
1127             grid_relayout(layer->grid, rect(0.0f, 0.0f,
1128                                             (float) event->window.data1,
1129                                             (float) event->window.data2));
1130         } break;
1131         }
1132     } break;
1133     }
1134
1135     switch (layer->state) {
1136     case RECT_LAYER_IDLE:
1137         return rect_layer_event_idle(layer, event, camera, undo_history);
1138
1139     case RECT_LAYER_CREATE:
1140         return rect_layer_event_create(layer, event, camera, undo_history);
1141
1142     case RECT_LAYER_RESIZE:
1143         return rect_layer_event_resize(layer, event, camera, undo_history);
1144
1145     case RECT_LAYER_MOVE:
1146         return rect_layer_event_move(layer, event, camera, undo_history);
1147
1148     case RECT_LAYER_ID_RENAME:
1149         return rect_layer_event_id_rename(layer, event, camera, undo_history);
1150
1151     case RECT_LAYER_RECOLOR:
1152         return rect_layer_event_recolor(layer, event, camera, undo_history);
1153     }
1154
1155
1156     return 0;
1157 }
1158
1159 size_t rect_layer_count(const RectLayer *layer)
1160 {
1161     return dynarray_count(layer->rects);
1162 }
1163
1164 const Rect *rect_layer_rects(const RectLayer *layer)
1165 {
1166     return dynarray_data(layer->rects);
1167 }
1168
1169 const Color *rect_layer_colors(const RectLayer *layer)
1170 {
1171     return dynarray_data(layer->colors);
1172 }
1173
1174 const char *rect_layer_ids(const RectLayer *layer)
1175 {
1176     return dynarray_data(layer->ids);
1177 }
1178
1179 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1180 {
1181     trace_assert(layer);
1182     trace_assert(filedump);
1183
1184     size_t n = dynarray_count(layer->ids);
1185     char *ids = dynarray_data(layer->ids);
1186     Rect *rects = dynarray_data(layer->rects);
1187     Color *colors = dynarray_data(layer->colors);
1188     Action *actions = dynarray_data(layer->actions);
1189
1190     fprintf(filedump, "%zd\n", n);
1191     for (size_t i = 0; i < n; ++i) {
1192         fprintf(filedump, "%s %f %f %f %f ",
1193                 ids + ENTITY_MAX_ID_SIZE * i,
1194                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1195         color_hex_to_stream(colors[i], filedump);
1196
1197         switch (actions[i].type) {
1198         case ACTION_NONE: {} break;
1199
1200         case ACTION_TOGGLE_GOAL:
1201         case ACTION_HIDE_LABEL: {
1202             fprintf(filedump, " %d %.*s",
1203                     (int)actions[i].type,
1204                     ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1205         } break;
1206         case ACTION_N: break;
1207         }
1208
1209         fprintf(filedump, "\n");
1210     }
1211
1212     return 0;
1213 }
1214
1215 const Action *rect_layer_actions(const RectLayer *layer)
1216 {
1217     return dynarray_data(layer->actions);
1218 }