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