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