]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
(#1003) Add undo move support to RectLayer
[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_ID_MAX_SIZE 36
17 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
18 #define CREATE_AREA_THRESHOLD 10.0
19
20 // TODO(#1003): RectLayer does not support UndoHistory
21
22 typedef enum {
23     RECT_LAYER_IDLE = 0,
24     RECT_LAYER_CREATE,
25     // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
26     RECT_LAYER_RESIZE,
27     RECT_LAYER_MOVE,
28     RECT_LAYER_ID_RENAME,
29 } RectLayerState;
30
31 struct RectLayer {
32     Lt *lt;
33     RectLayerState state;
34     Dynarray *ids;
35     Dynarray *rects;
36     Dynarray *colors;
37     ColorPicker color_picker;
38     Vec create_begin;
39     Vec create_end;
40     int selection;
41     Vec move_anchor;
42     Edit_field *id_edit_field;
43     Color prev_color;
44     Point prev_position;
45 };
46
47 typedef int (*EventHandler)(RectLayer *layer, const SDL_Event *event, const Camera *camera);
48
49 static
50 void rect_layer_undo_add(void *layer, Context context)
51 {
52     trace_assert(layer);
53     RectLayer *rect_layer = layer;
54
55     trace_assert(sizeof(size_t) < CONTEXT_SIZE);
56     size_t *index = (size_t *)context.data;
57
58     trace_assert(*index < dynarray_count(rect_layer->rects));
59     dynarray_delete_at(rect_layer->rects, *index);
60     dynarray_delete_at(rect_layer->colors, *index);
61     dynarray_delete_at(rect_layer->ids, *index);
62 }
63
64 static int rect_layer_add_rect(RectLayer *layer,
65                                Rect rect,
66                                Color color,
67                                UndoHistory *undo_history)
68 {
69     trace_assert(layer);
70
71     size_t index = dynarray_count(layer->rects);
72
73     if (dynarray_push(layer->rects, &rect) < 0) {
74         return -1;
75     }
76
77     if (dynarray_push(layer->colors, &color) < 0) {
78         return -1;
79     }
80
81     char id[RECT_LAYER_ID_MAX_SIZE];
82     for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
83         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
84     }
85     id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
86
87     if (dynarray_push(layer->ids, id)) {
88         return -1;
89     }
90
91     undo_history_push(
92         undo_history,
93         create_action(
94             layer,
95             rect_layer_undo_add,
96             &index, sizeof(index)));
97
98     return 0;
99 }
100
101 // TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
102 static int rect_layer_rect_at(RectLayer *layer, Vec position)
103 {
104     trace_assert(layer);
105
106     const size_t n = dynarray_count(layer->rects);
107     Rect *rects = dynarray_data(layer->rects);
108
109     for (size_t i = 0; i < n; ++i) {
110         if (rect_contains_point(rects[i], position)) {
111             return (int) i;
112         }
113     }
114
115     return -1;
116 }
117
118 static Rect rect_layer_resize_anchor(const RectLayer *layer, const Camera *camera, size_t i)
119 {
120     Rect *rects = dynarray_data(layer->rects);
121     const Rect overlay_rect =
122         rect_scale(
123             camera_rect(camera, rects[i]),
124             RECT_LAYER_SELECTION_THICCNESS * 0.5f);
125
126     return rect(
127         overlay_rect.x + overlay_rect.w,
128         overlay_rect.y + overlay_rect.h,
129         RECT_LAYER_SELECTION_THICCNESS * 2.0f,
130         RECT_LAYER_SELECTION_THICCNESS * 2.0f);
131 }
132
133 typedef struct {
134     Rect rect;
135     Color color;
136     char id[RECT_LAYER_ID_MAX_SIZE];
137     size_t index;
138 } DeleteContext;
139
140 static
141 DeleteContext rect_layer_create_delete_context(RectLayer *layer, size_t index)
142 {
143     trace_assert(layer);
144     trace_assert(index < dynarray_count(layer->rects));
145
146     DeleteContext context = {
147         .rect = *((Rect *)dynarray_pointer_at(layer->rects, index)),
148         .color = *((Color *)dynarray_pointer_at(layer->colors, index)),
149     };
150
151     memcpy(
152         context.id,
153         dynarray_pointer_at(layer->ids, index),
154         RECT_LAYER_ID_MAX_SIZE);
155
156     context.index = index;
157
158     return context;
159 }
160
161 static
162 void rect_layer_undo_delete(void *layer, Context context)
163 {
164     trace_assert(layer);
165     RectLayer *rect_layer = layer;
166
167     trace_assert(sizeof(DeleteContext) < CONTEXT_SIZE);
168     DeleteContext *delete_context = (DeleteContext *)context.data;
169
170     dynarray_insert_before(rect_layer->rects, delete_context->index, &delete_context->rect);
171     dynarray_insert_before(rect_layer->colors, delete_context->index, &delete_context->color);
172     dynarray_insert_before(rect_layer->ids, delete_context->index, &delete_context->id);
173 }
174
175 static int rect_layer_delete_rect_at(RectLayer *layer,
176                                      size_t i,
177                                      UndoHistory *undo_history)
178 {
179     trace_assert(layer);
180
181     DeleteContext context = rect_layer_create_delete_context(layer, i);
182
183     undo_history_push(
184         undo_history,
185         create_action(
186             layer,
187             rect_layer_undo_delete,
188             &context, sizeof(context)));
189
190     dynarray_delete_at(layer->rects, i);
191     dynarray_delete_at(layer->colors, i);
192     dynarray_delete_at(layer->ids, i);
193
194     return 0;
195 }
196
197 static int rect_layer_event_idle(RectLayer *layer,
198                                  const SDL_Event *event,
199                                  const Camera *camera,
200                                  UndoHistory *undo_history)
201 {
202     trace_assert(layer);
203     trace_assert(event);
204     trace_assert(camera);
205
206     switch (event->type) {
207     case SDL_MOUSEBUTTONDOWN: {
208         switch (event->button.button) {
209         case SDL_BUTTON_LEFT: {
210             Point position = camera_map_screen(
211                 camera,
212                 event->button.x,
213                 event->button.y);
214             int rect_at_position =
215                 rect_layer_rect_at(layer, position);
216
217             if (rect_at_position >= 0) {
218                 Rect *rects = dynarray_data(layer->rects);
219                 Color *colors = dynarray_data(layer->colors);
220                 layer->selection = rect_at_position;
221                 layer->state = RECT_LAYER_MOVE;
222                 layer->move_anchor =
223                     vec_sub(
224                         position,
225                         vec(
226                             rects[layer->selection].x,
227                             rects[layer->selection].y));
228                 layer->color_picker =
229                     create_color_picker_from_rgba(colors[rect_at_position]);
230                 layer->prev_color = colors[rect_at_position];
231                 layer->prev_position = vec(rects[rect_at_position].x, rects[rect_at_position].y);
232             } else if (layer->selection >= 0 && rect_contains_point(
233                            rect_layer_resize_anchor(
234                                layer,
235                                camera,
236                                (size_t)layer->selection),
237                            vec(
238                                (float) event->button.x,
239                                (float) event->button.y))) {
240                 layer->state = RECT_LAYER_RESIZE;
241             } else {
242                 layer->selection = rect_at_position;
243
244                 if (layer->selection < 0) {
245                     layer->state = RECT_LAYER_CREATE;
246                     layer->create_begin = position;
247                     layer->create_end = position;
248                 }
249             }
250         } break;
251         }
252     } break;
253
254     case SDL_KEYDOWN: {
255         switch (event->key.keysym.sym) {
256         case SDLK_DELETE: {
257             if (layer->selection >= 0) {
258                 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
259                 layer->selection = -1;
260             }
261         } break;
262
263         case SDLK_F2: {
264             if (layer->selection >= 0) {
265                 const char *ids = dynarray_data(layer->ids);
266                 layer->state = RECT_LAYER_ID_RENAME;
267                 edit_field_replace(
268                     layer->id_edit_field,
269                     ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
270                 SDL_StartTextInput();
271             }
272         } break;
273         }
274     } break;
275     }
276
277     return 0;
278 }
279
280 static int rect_layer_event_create(RectLayer *layer,
281                                    const SDL_Event *event,
282                                    const Camera *camera,
283                                    UndoHistory *undo_history)
284 {
285     trace_assert(layer);
286     trace_assert(event);
287     trace_assert(camera);
288
289     switch (event->type) {
290     case SDL_MOUSEBUTTONUP: {
291         switch (event->button.button) {
292         case SDL_BUTTON_LEFT: {
293             const Rect real_rect =
294                 rect_from_points(
295                     layer->create_begin,
296                     layer->create_end);
297             const float area = real_rect.w * real_rect.h;
298
299             if (area >= CREATE_AREA_THRESHOLD) {
300                 rect_layer_add_rect(
301                     layer,
302                     real_rect,
303                     color_picker_rgba(&layer->color_picker),
304                     undo_history);
305             } else {
306                 log_info("The area is too small %f. Such small box won't be created.\n", area);
307             }
308             layer->state = RECT_LAYER_IDLE;
309         } break;
310         }
311     } break;
312
313     case SDL_MOUSEMOTION: {
314         layer->create_end = camera_map_screen(
315             camera,
316             event->motion.x,
317             event->motion.y);
318     } break;
319     }
320     return 0;
321 }
322
323 static int rect_layer_event_resize(RectLayer *layer, const SDL_Event *event, const Camera *camera)
324 {
325     trace_assert(layer);
326     trace_assert(event);
327     trace_assert(camera);
328
329     switch (event->type) {
330     case SDL_MOUSEMOTION: {
331         Rect *rects = dynarray_data(layer->rects);
332         trace_assert(layer->selection >= 0);
333         rects[layer->selection] = rect_from_points(
334             vec(rects[layer->selection].x, rects[layer->selection].y),
335             vec_sum(
336                 camera_map_screen(
337                     camera,
338                     event->button.x,
339                     event->button.y),
340                 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
341                     RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
342     } break;
343
344     case SDL_MOUSEBUTTONUP: {
345         layer->state = RECT_LAYER_IDLE;
346     } break;
347     }
348
349     return 0;
350 }
351
352 typedef struct {
353     size_t index;
354     Point position;
355 } MoveContext;
356
357 static
358 void rect_layer_undo_move(void *layer, Context context)
359 {
360     trace_assert(layer);
361     RectLayer *rect_layer = layer;
362
363     trace_assert(sizeof(MoveContext) <= CONTEXT_SIZE);
364     MoveContext *move_context = (MoveContext *)context.data;
365
366     Rect *rect = dynarray_pointer_at(
367         rect_layer->rects,
368         move_context->index);
369
370     rect->x = move_context->position.x;
371     rect->y = move_context->position.y;
372 }
373
374 static int rect_layer_event_move(RectLayer *layer,
375                                  const SDL_Event *event,
376                                  const Camera *camera,
377                                  UndoHistory *undo_history)
378 {
379     trace_assert(layer);
380     trace_assert(event);
381     trace_assert(camera);
382
383     Rect *rects = dynarray_data(layer->rects);
384
385     switch (event->type) {
386     case SDL_MOUSEMOTION: {
387         Point position = vec_sub(
388             camera_map_screen(
389                 camera,
390                 event->button.x,
391                 event->button.y),
392             layer->move_anchor);
393
394         trace_assert(layer->selection >= 0);
395
396         rects[layer->selection].x = position.x;
397         rects[layer->selection].y = position.y;
398     } break;
399
400     case SDL_MOUSEBUTTONUP: {
401         layer->state = RECT_LAYER_IDLE;
402
403         MoveContext context = {
404             .index = (size_t)layer->selection,
405             .position = layer->prev_position
406         };
407
408         undo_history_push(
409             undo_history,
410             create_action(
411                 layer,
412                 rect_layer_undo_move,
413                 &context, sizeof(context)));
414
415         layer->prev_position = vec(
416             rects[layer->selection].x,
417             rects[layer->selection].y);
418     } break;
419     }
420     return 0;
421 }
422
423 static int rect_layer_event_id_rename(RectLayer *layer, const SDL_Event *event, const Camera *camera)
424 {
425     trace_assert(layer);
426     trace_assert(event);
427     trace_assert(camera);
428
429     switch (event->type) {
430     case SDL_KEYDOWN: {
431         switch (event->key.keysym.sym) {
432         case SDLK_RETURN: {
433             char *id =
434                 (char *)dynarray_data(layer->ids) + layer->selection * RECT_LAYER_ID_MAX_SIZE;
435             memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
436             memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
437             layer->state = RECT_LAYER_IDLE;
438             SDL_StopTextInput();
439         } break;
440
441         case SDLK_ESCAPE: {
442             layer->state = RECT_LAYER_IDLE;
443             SDL_StopTextInput();
444         } break;
445         }
446     } break;
447     }
448
449     return edit_field_event(layer->id_edit_field, event);
450 }
451
452 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
453 {
454     LayerPtr layer = {
455         .type = LAYER_RECT,
456         .ptr = rect_layer
457     };
458     return layer;
459 }
460
461 RectLayer *create_rect_layer(void)
462 {
463     Lt *lt = create_lt();
464
465     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
466     if (layer == NULL) {
467         RETURN_LT(lt, NULL);
468     }
469     layer->lt = lt;
470
471     layer->ids = PUSH_LT(
472         lt,
473         create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
474         destroy_dynarray);
475     if (layer->ids == NULL) {
476         RETURN_LT(lt, NULL);
477     }
478
479     layer->rects = PUSH_LT(
480         lt,
481         create_dynarray(sizeof(Rect)),
482         destroy_dynarray);
483     if (layer->rects == NULL) {
484         RETURN_LT(lt, NULL);
485     }
486
487     layer->colors = PUSH_LT(
488         lt,
489         create_dynarray(sizeof(Color)),
490         destroy_dynarray);
491     if (layer->colors == NULL) {
492         RETURN_LT(lt, NULL);
493     }
494
495     layer->id_edit_field = PUSH_LT(
496         lt,
497         create_edit_field(
498             vec(3.0f, 3.0f),
499             COLOR_BLACK),
500         destroy_edit_field);
501     if (layer->id_edit_field == NULL) {
502         RETURN_LT(lt, NULL);
503     }
504
505     Color init_color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
506     layer->color_picker = create_color_picker_from_rgba(init_color);
507     layer->prev_color = init_color;
508     layer->selection = -1;
509
510     return layer;
511 }
512
513 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
514 {
515     trace_assert(line_stream);
516
517     RectLayer *layer = create_rect_layer();
518     if (layer == NULL) {
519         return NULL;
520     }
521
522     const char *line = line_stream_next(line_stream);
523     if (line == NULL) {
524         RETURN_LT(layer->lt, NULL);
525     }
526
527     size_t count = 0;
528     if (sscanf(line, "%zu", &count) < 0) {
529         RETURN_LT(layer->lt, NULL);
530     }
531
532     for (size_t i = 0; i < count; ++i) {
533         line = line_stream_next(line_stream);
534         if (line == NULL) {
535             RETURN_LT(layer->lt, NULL);
536         }
537
538         char hex[7];
539         Rect rect;
540         char id[RECT_LAYER_ID_MAX_SIZE];
541
542         if (sscanf(line,
543                    "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
544                    id,
545                    &rect.x, &rect.y,
546                    &rect.w, &rect.h,
547                    hex) < 0) {
548             RETURN_LT(layer->lt, NULL);
549         }
550
551         Color color = hexstr(hex);
552
553         dynarray_push(layer->rects, &rect);
554         dynarray_push(layer->ids, id);
555         dynarray_push(layer->colors, &color);
556     }
557
558     return layer;
559 }
560
561 void destroy_rect_layer(RectLayer *layer)
562 {
563     trace_assert(layer);
564     RETURN_LT0(layer->lt);
565 }
566
567 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
568 {
569     trace_assert(layer);
570     trace_assert(camera);
571
572     const size_t n = dynarray_count(layer->rects);
573     Rect *rects = dynarray_data(layer->rects);
574     Color *colors = dynarray_data(layer->colors);
575     const char *ids = dynarray_data(layer->ids);
576
577     // The Rectangles
578     for (size_t i = 0; i < n; ++i) {
579         if (camera_fill_rect(
580                 camera,
581                 rects[i],
582                 color_scale(
583                     colors[i],
584                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
585             return -1;
586         }
587     }
588
589     // Proto Rectangle
590     const Color color = color_picker_rgba(&layer->color_picker);
591     if (layer->state == RECT_LAYER_CREATE) {
592         if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
593             return -1;
594         }
595     }
596
597     // ID renaming Edit Field
598     if (layer->state == RECT_LAYER_ID_RENAME) {
599         if (edit_field_render_screen(layer->id_edit_field, camera, vec(400.0f, 400.0f)) < 0) {
600             return -1;
601         }
602     }
603
604     // Selection Overlay
605     if (active && layer->selection >= 0) {
606         const Rect overlay_rect =
607             rect_scale(
608                 camera_rect(camera, rects[layer->selection]),
609                 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
610         const Color overlay_color = color_invert(colors[layer->selection]);
611
612         // Main Rectangle
613         if (camera_fill_rect(
614                 camera,
615                 rects[layer->selection],
616                 color_scale(
617                     colors[layer->selection],
618                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
619             return -1;
620         }
621
622         if (camera_draw_thicc_rect_screen(
623                 camera,
624                 overlay_rect,
625                 overlay_color,
626                 RECT_LAYER_SELECTION_THICCNESS) < 0) {
627             return -1;
628         }
629
630         // Rectangle Id
631         if (camera_render_text(
632                 camera,
633                 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
634                 vec(3.0f, 3.0f),
635                 color_invert(colors[layer->selection]),
636                 rect_position(rects[layer->selection])) < 0) {
637             return -1;
638         }
639
640         // Resize Anchor
641         if (camera_fill_rect_screen(
642                 camera,
643                 rect_layer_resize_anchor(layer, camera, (size_t) layer->selection),
644                 overlay_color) < 0) {
645             return -1;
646         }
647     }
648
649     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
650         return -1;
651     }
652
653     return 0;
654 }
655
656 typedef struct {
657     Color color;
658     size_t index;
659 } ColorContext;
660
661 static
662 void rect_layer_undo_color(void *layer, Context context)
663 {
664     trace_assert(layer);
665     RectLayer *rect_layer = layer;
666
667     trace_assert(sizeof(ColorContext) < CONTEXT_SIZE);
668     ColorContext *color_context = (ColorContext *)context.data;
669     trace_assert(color_context->index < dynarray_count(rect_layer->rects));
670
671     dynarray_replace_at(
672         rect_layer->colors,
673         color_context->index,
674         &color_context->color);
675 }
676
677 int rect_layer_event(RectLayer *layer,
678                      const SDL_Event *event,
679                      const Camera *camera,
680                      UndoHistory *undo_history)
681 {
682     trace_assert(layer);
683     trace_assert(event);
684     trace_assert(undo_history);
685
686     int color_changed = 0;
687     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
688         return -1;
689     }
690
691     if (color_changed) {
692         if (layer->selection >= 0) {
693             Color *colors = dynarray_data(layer->colors);
694             colors[layer->selection] = color_picker_rgba(&layer->color_picker);
695
696             if (!color_picker_drag(&layer->color_picker)) {
697                 ColorContext context = {
698                     .color = layer->prev_color,
699                     .index = (size_t) layer->selection
700                 };
701
702                 undo_history_push(
703                     undo_history,
704                     create_action(
705                         layer,
706                         rect_layer_undo_color,
707                         &context,
708                         sizeof(context)));
709                 layer->prev_color = colors[layer->selection];
710             }
711         }
712
713         return 0;
714     }
715
716     switch (layer->state) {
717     case RECT_LAYER_IDLE:
718         return rect_layer_event_idle(layer, event, camera, undo_history);
719
720     case RECT_LAYER_CREATE:
721         return rect_layer_event_create(layer, event, camera, undo_history);
722
723     case RECT_LAYER_RESIZE:
724         return rect_layer_event_resize(layer, event, camera);
725
726     case RECT_LAYER_MOVE:
727         return rect_layer_event_move(layer, event, camera, undo_history);
728
729     case RECT_LAYER_ID_RENAME:
730         return rect_layer_event_id_rename(layer, event, camera);
731     }
732
733     return 0;
734 }
735
736 size_t rect_layer_count(const RectLayer *layer)
737 {
738     return dynarray_count(layer->rects);
739 }
740
741 const Rect *rect_layer_rects(const RectLayer *layer)
742 {
743     return dynarray_data(layer->rects);
744 }
745
746 const Color *rect_layer_colors(const RectLayer *layer)
747 {
748     return dynarray_data(layer->colors);
749 }
750
751 const char *rect_layer_ids(const RectLayer *layer)
752 {
753     return dynarray_data(layer->ids);
754 }
755
756 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
757 {
758     trace_assert(layer);
759     trace_assert(filedump);
760
761     size_t n = dynarray_count(layer->ids);
762     char *ids = dynarray_data(layer->ids);
763     Rect *rects = dynarray_data(layer->rects);
764     Color *colors = dynarray_data(layer->colors);
765
766     fprintf(filedump, "%zd\n", n);
767     for (size_t i = 0; i < n; ++i) {
768         fprintf(filedump, "%s %f %f %f %f ",
769                 ids + RECT_LAYER_ID_MAX_SIZE * i,
770                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
771         color_hex_to_stream(colors[i], filedump);
772         fprintf(filedump, "\n");
773     }
774
775     return 0;
776 }