]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
(#1118) Try to fix warnings on MacOS build
[nothing.git] / src / game / level / level_editor / point_layer.c
1 #include <stdio.h>
2
3 #include <SDL.h>
4
5 #include "dynarray.h"
6 #include "game/camera.h"
7 #include "system/line_stream.h"
8 #include "system/log.h"
9 #include "system/lt.h"
10 #include "system/nth_alloc.h"
11 #include "system/stacktrace.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14 #include "./point_layer.h"
15 #include "math/extrema.h"
16 #include "math/mat3x3.h"
17 #include "./color_picker.h"
18 #include "undo_history.h"
19
20 #define POINT_LAYER_ELEMENT_RADIUS 10.0f
21 #define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
22 #define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
23
24 static int clipboard = 0;
25 static Color clipboard_color;
26
27 typedef enum {
28     POINT_LAYER_IDLE = 0,
29     POINT_LAYER_EDIT_ID,
30     POINT_LAYER_MOVE,
31     POINT_LAYER_RECOLOR
32 } PointLayerState;
33
34 struct PointLayer
35 {
36     Lt *lt;
37     PointLayerState state;
38     Dynarray/*<Vec2f>*/ *positions;
39     Dynarray/*<Color>*/ *colors;
40     Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
41     int selection;
42     ColorPicker color_picker;
43
44     Vec2f inter_position;
45     Color inter_color;
46     Edit_field *edit_field;
47
48     int id_name_counter;
49     const char *id_name_prefix;
50 };
51
52 typedef enum {
53     UNDO_ADD,
54     UNDO_DELETE,
55     UNDO_UPDATE,
56     UNDO_SWAP
57 } UndoType;
58
59 typedef struct {
60     UndoType type;
61     PointLayer *layer;
62     Vec2f position;
63     Color color;
64     char id[ID_MAX_SIZE];
65     size_t index;
66     size_t index2;
67 } UndoContext;
68
69 static
70 UndoContext create_undo_swap_context(PointLayer *point_layer,
71                                      size_t index, size_t index2)
72 {
73     trace_assert(point_layer);
74     trace_assert(index < dynarray_count(point_layer->positions));
75     trace_assert(index2 < dynarray_count(point_layer->positions));
76
77     UndoContext undo_context;
78     undo_context.type = UNDO_SWAP;
79     undo_context.layer = point_layer;
80     undo_context.index = index;
81     undo_context.index2 = index2;
82     return undo_context;
83 }
84
85 static
86 UndoContext create_undo_context(PointLayer *point_layer,
87                                 UndoType type)
88 {
89     trace_assert(type != UNDO_SWAP);
90
91     (void) create_undo_swap_context;
92
93     UndoContext undo_context;
94
95     size_t index =
96         type == UNDO_ADD
97         ? dynarray_count(point_layer->positions) - 1
98         : (size_t) point_layer->selection;
99
100     undo_context.type = type;
101     undo_context.layer = point_layer;
102     dynarray_copy_to(point_layer->positions, &undo_context.position, index);
103     dynarray_copy_to(point_layer->colors, &undo_context.color, index);
104     dynarray_copy_to(point_layer->ids, &undo_context.id, index);
105     undo_context.index = index;
106
107     return undo_context;
108 }
109
110 static
111 void point_layer_undo(void *context, size_t context_size)
112 {
113     trace_assert(context);
114     trace_assert(sizeof(UndoContext) == context_size);
115
116     UndoContext *undo_context = context;
117     PointLayer *point_layer = undo_context->layer;
118
119     switch (undo_context->type) {
120     case UNDO_ADD: {
121         dynarray_pop(point_layer->positions, NULL);
122         dynarray_pop(point_layer->colors, NULL);
123         dynarray_pop(point_layer->ids, NULL);
124         point_layer->selection = -1;
125     } break;
126
127     case UNDO_DELETE: {
128         dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position);
129         dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color);
130         dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id);
131         point_layer->selection = -1;
132     } break;
133
134     case UNDO_UPDATE: {
135         dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position);
136         dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color);
137         dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id);
138     } break;
139
140     case UNDO_SWAP: {
141         dynarray_swap(point_layer->positions, undo_context->index, undo_context->index2);
142         dynarray_swap(point_layer->colors, undo_context->index, undo_context->index2);
143         dynarray_swap(point_layer->ids, undo_context->index, undo_context->index2);
144     } break;
145     }
146 }
147
148 #define UNDO_PUSH(HISTORY, CONTEXT)                                     \
149     do {                                                                \
150         UndoContext context = (CONTEXT);                                \
151         undo_history_push(                                              \
152             HISTORY,                                                    \
153             point_layer_undo,                                           \
154             &context,                                                   \
155             sizeof(context));                                           \
156     } while(0)
157
158 LayerPtr point_layer_as_layer(PointLayer *point_layer)
159 {
160     LayerPtr layer = {
161         .type = LAYER_POINT,
162         .ptr = point_layer
163     };
164     return layer;
165 }
166
167 PointLayer *create_point_layer(const char *id_name_prefix)
168 {
169     Lt *lt = create_lt();
170
171     PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
172     if (point_layer == NULL) {
173         RETURN_LT(lt, NULL);
174     }
175     point_layer->lt = lt;
176
177     point_layer->state = POINT_LAYER_IDLE;
178
179     point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Vec2f)), destroy_dynarray);
180     if (point_layer->positions == NULL) {
181         RETURN_LT(lt, NULL);
182     }
183
184     point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
185     if (point_layer->colors == NULL) {
186         RETURN_LT(lt, NULL);
187     }
188
189     point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
190     if (point_layer->ids == NULL) {
191         RETURN_LT(lt, NULL);
192     }
193
194     point_layer->edit_field = PUSH_LT(
195         lt,
196         create_edit_field(
197             POINT_LAYER_ID_TEXT_SIZE,
198             POINT_LAYER_ID_TEXT_COLOR),
199         destroy_edit_field);
200     if (point_layer->edit_field == NULL) {
201         RETURN_LT(lt, NULL);
202     }
203
204     point_layer->id_name_prefix = id_name_prefix;
205
206     return point_layer;
207 }
208
209 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream,
210                                                 const char *id_name_prefix)
211 {
212     trace_assert(line_stream);
213
214     PointLayer *point_layer = create_point_layer(id_name_prefix);
215
216     size_t count = 0;
217     if (sscanf(
218             line_stream_next(line_stream),
219             "%zu",
220             &count) == EOF) {
221         log_fail("Could not read amount of points");
222         RETURN_LT(point_layer->lt, NULL);
223     }
224
225     char color_name[7];
226     char id[ID_MAX_SIZE];
227     float x, y;
228     for (size_t i = 0; i < count; ++i) {
229         if (sscanf(
230                 line_stream_next(line_stream),
231                 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
232                 id, &x, &y, color_name) < 0) {
233             log_fail("Could not read %dth goal\n", i);
234             RETURN_LT(point_layer->lt, NULL);
235         }
236         const Color color = hexstr(color_name);
237         const Vec2f point = vec(x, y);
238
239         dynarray_push(point_layer->colors, &color);
240         dynarray_push(point_layer->positions, &point);
241         dynarray_push(point_layer->ids, id);
242     }
243
244     point_layer->selection = -1;
245
246     point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
247
248     return point_layer;
249 }
250
251 void destroy_point_layer(PointLayer *point_layer)
252 {
253     trace_assert(point_layer);
254     RETURN_LT0(point_layer->lt);
255 }
256
257 static inline
258 Triangle element_shape(Vec2f position, float scale)
259 {
260     return triangle_mat3x3_product(
261         equilateral_triangle(),
262         mat3x3_product(
263             trans_mat_vec(position),
264             scale_mat(scale)));
265 }
266
267 int point_layer_render(const PointLayer *point_layer,
268                        const Camera *camera,
269                        int active)
270 {
271     trace_assert(point_layer);
272     trace_assert(camera);
273
274     const int n = (int) dynarray_count(point_layer->positions);
275     Vec2f *positions = dynarray_data(point_layer->positions);
276     Color *colors = dynarray_data(point_layer->colors);
277     char *ids = dynarray_data(point_layer->ids);
278
279     for (int i = 0; i < n; ++i) {
280         const Color color = color_scale(
281             point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
282             ? point_layer->inter_color
283             : colors[i],
284             rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
285
286         const Vec2f position =
287             point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
288             ? point_layer->inter_position
289             : positions[i];
290
291         // Selection Layer
292         if (active && i == point_layer->selection) {
293             if (camera_fill_triangle(
294                     camera,
295                     element_shape(
296                         position,
297                         POINT_LAYER_ELEMENT_RADIUS + 5.0f),
298                     color_invert(color)) < 0) {
299                 return -1;
300             }
301
302             if (point_layer->state != POINT_LAYER_EDIT_ID &&
303                 camera_render_text(
304                     camera,
305                     ids + ID_MAX_SIZE * i,
306                     POINT_LAYER_ID_TEXT_SIZE,
307                     POINT_LAYER_ID_TEXT_COLOR,
308                     position) < 0) {
309                 return -1;
310             }
311         }
312
313         if (camera_fill_triangle(
314                 camera,
315                 element_shape(
316                     position,
317                     POINT_LAYER_ELEMENT_RADIUS),
318                 color) < 0) {
319             return -1;
320         }
321     }
322
323     if (point_layer->state == POINT_LAYER_EDIT_ID) {
324         if (edit_field_render_world(
325                 point_layer->edit_field,
326                 camera,
327                 positions[point_layer->selection]) < 0) {
328             return -1;
329         }
330     }
331
332     if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
333         return -1;
334     }
335
336
337     return 0;
338 }
339
340 static
341 int point_layer_element_at(const PointLayer *point_layer,
342                            Vec2f position)
343 {
344     trace_assert(point_layer);
345
346     int n = (int) dynarray_count(point_layer->positions);
347     Vec2f *positions = dynarray_data(point_layer->positions);
348
349     for (int i = n - 1; i >= 0; --i) {
350         if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
351             return i;
352         }
353     }
354
355     return -1;
356 }
357
358 static
359 int point_layer_add_element(PointLayer *point_layer,
360                             Vec2f position,
361                             Color color,
362                             UndoHistory *undo_history)
363 {
364     trace_assert(point_layer);
365     trace_assert(undo_history);
366
367     char id[ID_MAX_SIZE];
368     snprintf(id, ID_MAX_SIZE, "%s_%d",
369              point_layer->id_name_prefix,
370              point_layer->id_name_counter++);
371
372     dynarray_push(point_layer->positions, &position);
373     dynarray_push(point_layer->colors, &color);
374     dynarray_push(point_layer->ids, id);
375
376     UNDO_PUSH(
377         undo_history,
378         create_undo_context(point_layer, UNDO_ADD));
379
380     return 0;
381 }
382
383 static
384 void point_layer_swap_elements(PointLayer *point_layer,
385                                size_t a, size_t b,
386                                UndoHistory *undo_history)
387 {
388     trace_assert(point_layer);
389     trace_assert(undo_history);
390     trace_assert(a < dynarray_count(point_layer->positions));
391     trace_assert(b < dynarray_count(point_layer->positions));
392
393     dynarray_swap(point_layer->positions, a, b);
394     dynarray_swap(point_layer->colors, a, b);
395     dynarray_swap(point_layer->ids, a, b);
396
397     UNDO_PUSH(
398         undo_history,
399         create_undo_swap_context(point_layer, a, b));
400 }
401
402 static
403 void point_layer_delete_nth_element(PointLayer *point_layer,
404                                     size_t i,
405                                     UndoHistory *undo_history)
406 {
407     trace_assert(point_layer);
408
409     UNDO_PUSH(
410         undo_history,
411         create_undo_context(
412             point_layer,
413             UNDO_DELETE));
414
415     dynarray_delete_at(point_layer->positions, i);
416     dynarray_delete_at(point_layer->colors, i);
417     dynarray_delete_at(point_layer->ids, i);
418 }
419
420 static
421 int point_layer_idle_event(PointLayer *point_layer,
422                            const SDL_Event *event,
423                            const Camera *camera,
424                            UndoHistory *undo_history)
425 {
426     trace_assert(point_layer);
427     trace_assert(event);
428     trace_assert(camera);
429
430     int selected = 0;
431     if (color_picker_event(
432             &point_layer->color_picker,
433             event,
434             camera,
435             &selected) < 0) {
436         return -1;
437     }
438
439     if (selected) {
440         if (point_layer->selection >= 0) {
441             point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
442             point_layer->state = POINT_LAYER_RECOLOR;
443         }
444         return 0;
445     }
446
447     switch (event->type) {
448     case SDL_MOUSEBUTTONDOWN: {
449         switch (event->button.button) {
450         case SDL_BUTTON_LEFT: {
451             const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
452
453             point_layer->selection = point_layer_element_at(
454                 point_layer, position);
455
456             if (point_layer->selection < 0) {
457                 point_layer_add_element(
458                     point_layer,
459                     position,
460                     color_picker_rgba(&point_layer->color_picker),
461                     undo_history);
462             } else {
463                 Color *colors = dynarray_data(point_layer->colors);
464                 Vec2f *positions = dynarray_data(point_layer->positions);
465
466                 point_layer->state = POINT_LAYER_MOVE;
467                 point_layer->color_picker =
468                     create_color_picker_from_rgba(colors[point_layer->selection]);
469                 point_layer->inter_position = positions[point_layer->selection];
470             }
471         } break;
472         }
473     } break;
474
475     case SDL_KEYDOWN: {
476         switch (event->key.keysym.sym) {
477         case SDLK_UP: {
478             if ((event->key.keysym.mod & KMOD_SHIFT)
479                 && (point_layer->selection >= 0)
480                 && ((size_t)(point_layer->selection + 1) < dynarray_count(point_layer->positions))) {
481                 point_layer_swap_elements(
482                     point_layer,
483                     (size_t) point_layer->selection,
484                     (size_t) point_layer->selection + 1,
485                     undo_history);
486                 point_layer->selection++;
487             }
488         } break;
489
490         case SDLK_DOWN: {
491             if ((event->key.keysym.mod & KMOD_SHIFT)
492                 && (point_layer->selection > 0)
493                 && ((size_t) point_layer->selection < dynarray_count(point_layer->positions))) {
494                 point_layer_swap_elements(
495                     point_layer,
496                     (size_t) point_layer->selection,
497                     (size_t) point_layer->selection - 1,
498                     undo_history);
499                 point_layer->selection--;
500             }
501         } break;
502
503         case SDLK_DELETE: {
504             if (0 <= point_layer->selection && point_layer->selection < (int) dynarray_count(point_layer->positions)) {
505                 point_layer_delete_nth_element(
506                     point_layer,
507                     (size_t)point_layer->selection,
508                     undo_history);
509                 point_layer->selection = -1;
510             }
511         } break;
512
513         case SDLK_F2: {
514             if (point_layer->selection >= 0) {
515                 char *ids = dynarray_data(point_layer->ids);
516                 point_layer->state = POINT_LAYER_EDIT_ID;
517                 edit_field_replace(
518                     point_layer->edit_field,
519                     ids + ID_MAX_SIZE * point_layer->selection);
520                 SDL_StartTextInput();
521             }
522         } break;
523
524         case SDLK_c: {
525             if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
526                 clipboard = 1;
527                 dynarray_copy_to(point_layer->colors, &clipboard_color, (size_t)point_layer->selection);
528             }
529         } break;
530
531         case SDLK_v: {
532             if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
533                 int x, y;
534                 SDL_GetMouseState(&x, &y);
535                 Vec2f position = camera_map_screen(camera, x, y);
536
537                 point_layer_add_element(
538                     point_layer,
539                     position,
540                     clipboard_color,
541                     undo_history);
542             }
543         } break;
544         }
545     } break;
546     }
547
548     return 0;
549 }
550
551 static
552 int point_layer_edit_id_event(PointLayer *point_layer,
553                               const SDL_Event *event,
554                               const Camera *camera,
555                               UndoHistory *undo_history)
556 {
557     trace_assert(point_layer);
558     trace_assert(event);
559     trace_assert(camera);
560
561     switch (event->type) {
562     case SDL_KEYDOWN: {
563         switch(event->key.keysym.sym) {
564         case SDLK_RETURN: {
565             UNDO_PUSH(
566                 undo_history,
567                 create_undo_context(
568                     point_layer,
569                     UNDO_UPDATE));
570
571             char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selection);
572             const char *text = edit_field_as_text(point_layer->edit_field);
573             size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
574             memcpy(id, text, n);
575             memset(id + n, 0, ID_MAX_SIZE - n);
576
577             point_layer->state = POINT_LAYER_IDLE;
578             SDL_StopTextInput();
579             return 0;
580         } break;
581
582         case SDLK_ESCAPE: {
583             point_layer->state = POINT_LAYER_IDLE;
584             SDL_StopTextInput();
585             return 0;
586         } break;
587         }
588     } break;
589     }
590
591     return edit_field_event(point_layer->edit_field, event);
592 }
593
594 static
595 int point_layer_move_event(PointLayer *point_layer,
596                            const SDL_Event *event,
597                            const Camera *camera,
598                            UndoHistory *undo_history)
599 {
600     trace_assert(point_layer);
601     trace_assert(event);
602     trace_assert(camera);
603     trace_assert(point_layer->selection >= 0);
604
605     Vec2f *positions = dynarray_data(point_layer->positions);
606
607     switch (event->type) {
608     case SDL_MOUSEBUTTONUP: {
609         switch (event->button.button) {
610         case SDL_BUTTON_LEFT: {
611             point_layer->state = POINT_LAYER_IDLE;
612
613             const float distance = vec_length(
614                 vec_sub(point_layer->inter_position,
615                         positions[point_layer->selection]));
616
617             if (distance > 1e-6) {
618                 UNDO_PUSH(
619                     undo_history,
620                     create_undo_context(
621                         point_layer,
622                         UNDO_UPDATE));
623
624                 dynarray_replace_at(
625                     point_layer->positions,
626                     (size_t) point_layer->selection,
627                     &point_layer->inter_position);
628             }
629         } break;
630         }
631     } break;
632
633     case SDL_MOUSEMOTION: {
634         const Uint8 *state = SDL_GetKeyboardState(NULL);
635         const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
636         const Vec2f point_pos = positions[point_layer->selection];
637
638         if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
639             point_layer->inter_position = mouse_pos;
640         } else {
641             const float dx = fabsf(point_pos.x - mouse_pos.x);
642             const float dy = fabsf(point_pos.y - mouse_pos.y);
643
644             if (dx > dy) {
645                 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
646             } else {
647                 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
648             }
649         }
650     } break;
651     }
652
653     return 0;
654 }
655
656 static
657 int point_layer_recolor_event(PointLayer *point_layer,
658                               const SDL_Event *event,
659                               const Camera *camera,
660                               UndoHistory *undo_history)
661 {
662     trace_assert(point_layer);
663     trace_assert(event);
664     trace_assert(camera);
665     trace_assert(undo_history);
666     trace_assert(point_layer->selection >= 0);
667
668     int selected = 0;
669     if (color_picker_event(
670             &point_layer->color_picker,
671             event,
672             camera,
673             &selected) < 0) {
674         return -1;
675     }
676
677     if (selected) {
678         point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
679
680         if (!color_picker_drag(&point_layer->color_picker)) {
681             UNDO_PUSH(
682                 undo_history,
683                 create_undo_context(
684                     point_layer,
685                     UNDO_UPDATE));
686
687             dynarray_replace_at(
688                 point_layer->colors,
689                 (size_t) point_layer->selection,
690                 &point_layer->inter_color);
691
692             point_layer->state = POINT_LAYER_IDLE;
693         }
694     }
695
696
697     return 0;
698 }
699
700 int point_layer_event(PointLayer *point_layer,
701                       const SDL_Event *event,
702                       const Camera *camera,
703                       UndoHistory *undo_history)
704 {
705     trace_assert(point_layer);
706     trace_assert(event);
707     trace_assert(camera);
708     trace_assert(undo_history);
709
710     switch (point_layer->state) {
711     case POINT_LAYER_IDLE:
712         return point_layer_idle_event(point_layer, event, camera, undo_history);
713
714     case POINT_LAYER_EDIT_ID:
715         return point_layer_edit_id_event(point_layer, event, camera, undo_history);
716
717     case POINT_LAYER_MOVE:
718         return point_layer_move_event(point_layer, event, camera, undo_history);
719
720     case POINT_LAYER_RECOLOR:
721         return point_layer_recolor_event(point_layer, event, camera, undo_history);
722     }
723
724     return 0;
725 }
726
727 size_t point_layer_count(const PointLayer *point_layer)
728 {
729     trace_assert(point_layer);
730     return dynarray_count(point_layer->positions);
731 }
732
733 const Vec2f *point_layer_positions(const PointLayer *point_layer)
734 {
735     trace_assert(point_layer);
736     return dynarray_data(point_layer->positions);
737 }
738
739 const Color *point_layer_colors(const PointLayer *point_layer)
740 {
741     trace_assert(point_layer);
742     return dynarray_data(point_layer->colors);
743 }
744
745 const char *point_layer_ids(const PointLayer *point_layer)
746 {
747     trace_assert(point_layer);
748     return dynarray_data(point_layer->ids);
749 }
750
751 int point_layer_dump_stream(const PointLayer *point_layer,
752                             FILE *filedump)
753 {
754     trace_assert(point_layer);
755     trace_assert(filedump);
756
757     size_t n = dynarray_count(point_layer->ids);
758     char *ids = dynarray_data(point_layer->ids);
759     Vec2f *positions = dynarray_data(point_layer->positions);
760     Color *colors = dynarray_data(point_layer->colors);
761
762     fprintf(filedump, "%zd\n", n);
763     for (size_t i = 0; i < n; ++i) {
764         fprintf(filedump, "%s %f %f ",
765                 ids + ID_MAX_SIZE * i,
766                 positions[i].x, positions[i].y);
767         color_hex_to_stream(colors[i], filedump);
768         fprintf(filedump, "\n");
769     }
770
771     return 0;
772 }