]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
Delete LineStream from the existance
[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 = PUSH_LT(
185         lt,
186         create_edit_field(
187             POINT_LAYER_ID_TEXT_SIZE,
188             POINT_LAYER_ID_TEXT_COLOR),
189         destroy_edit_field);
190     if (point_layer->edit_field == NULL) {
191         RETURN_LT(lt, NULL);
192     }
193
194     point_layer->id_name_prefix = id_name_prefix;
195
196     return point_layer;
197 }
198
199 PointLayer *chop_point_layer(Memory *memory,
200                              String *input,
201                              const char *id_name_prefix)
202 {
203     trace_assert(memory);
204     trace_assert(input);
205     trace_assert(id_name_prefix);
206
207     PointLayer *point_layer = create_point_layer(id_name_prefix);
208
209     int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
210     char id[ENTITY_MAX_ID_SIZE];
211     for (int i = 0; i < n; ++i) {
212         String line = trim(chop_by_delim(input, '\n'));
213         String string_id = trim(chop_word(&line));
214         Vec2f point;
215         point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
216         point.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
217         Color color = hexs(trim(chop_word(&line)));
218
219         memset(id, 0, ENTITY_MAX_ID_SIZE);
220         memcpy(
221             id,
222             string_id.data,
223             min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
224
225         dynarray_push(&point_layer->positions, &point);
226         dynarray_push(&point_layer->colors, &color);
227         dynarray_push(&point_layer->ids, id);
228     }
229
230     point_layer->selection = -1;
231     point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
232
233     return point_layer;
234 }
235
236 void destroy_point_layer(PointLayer *point_layer)
237 {
238     trace_assert(point_layer);
239
240     free(point_layer->positions.data);
241     free(point_layer->colors.data);
242     free(point_layer->ids.data);
243
244     RETURN_LT0(point_layer->lt);
245 }
246
247 static inline
248 Triangle element_shape(Vec2f position, float scale)
249 {
250     return triangle_mat3x3_product(
251         equilateral_triangle(),
252         mat3x3_product(
253             trans_mat_vec(position),
254             scale_mat(scale)));
255 }
256
257 int point_layer_render(const PointLayer *point_layer,
258                        const Camera *camera,
259                        int active)
260 {
261     trace_assert(point_layer);
262     trace_assert(camera);
263
264     const int n = (int)point_layer->positions.count;
265     Vec2f *positions = (Vec2f *)point_layer->positions.data;
266     Color *colors = (Color *)point_layer->colors.data;
267     char *ids = (char *)point_layer->ids.data;
268
269     for (int i = 0; i < n; ++i) {
270         const Color color = color_scale(
271             point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
272             ? point_layer->inter_color
273             : colors[i],
274             rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
275
276         const Vec2f position =
277             point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
278             ? point_layer->inter_position
279             : positions[i];
280
281         // Selection Layer
282         if (active && i == point_layer->selection) {
283             if (camera_fill_triangle(
284                     camera,
285                     element_shape(
286                         position,
287                         POINT_LAYER_ELEMENT_RADIUS + 5.0f),
288                     color_invert(color)) < 0) {
289                 return -1;
290             }
291
292             if (point_layer->state != POINT_LAYER_EDIT_ID &&
293                 camera_render_text(
294                     camera,
295                     ids + ID_MAX_SIZE * i,
296                     POINT_LAYER_ID_TEXT_SIZE,
297                     POINT_LAYER_ID_TEXT_COLOR,
298                     position) < 0) {
299                 return -1;
300             }
301         }
302
303         if (camera_fill_triangle(
304                 camera,
305                 element_shape(
306                     position,
307                     POINT_LAYER_ELEMENT_RADIUS),
308                 color) < 0) {
309             return -1;
310         }
311     }
312
313     if (point_layer->state == POINT_LAYER_EDIT_ID) {
314         if (edit_field_render_world(
315                 point_layer->edit_field,
316                 camera,
317                 positions[point_layer->selection]) < 0) {
318             return -1;
319         }
320     }
321
322     if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
323         return -1;
324     }
325
326
327     return 0;
328 }
329
330 static
331 int point_layer_element_at(const PointLayer *point_layer,
332                            Vec2f position)
333 {
334     trace_assert(point_layer);
335
336     int n = (int) point_layer->positions.count;
337     Vec2f *positions = (Vec2f *)point_layer->positions.data;
338
339     for (int i = n - 1; i >= 0; --i) {
340         if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
341             return i;
342         }
343     }
344
345     return -1;
346 }
347
348 static
349 int point_layer_add_element(PointLayer *point_layer,
350                             Vec2f position,
351                             Color color,
352                             UndoHistory *undo_history)
353 {
354     trace_assert(point_layer);
355     trace_assert(undo_history);
356
357     char id[ID_MAX_SIZE];
358     snprintf(id, ID_MAX_SIZE, "%s_%d",
359              point_layer->id_name_prefix,
360              point_layer->id_name_counter++);
361
362     dynarray_push(&point_layer->positions, &position);
363     dynarray_push(&point_layer->colors, &color);
364     dynarray_push(&point_layer->ids, id);
365
366     POINT_UNDO_PUSH(
367         undo_history,
368         create_point_undo_context(point_layer, POINT_UNDO_ADD));
369
370     return 0;
371 }
372
373 static
374 void point_layer_swap_elements(PointLayer *point_layer,
375                                size_t a, size_t b,
376                                UndoHistory *undo_history)
377 {
378     trace_assert(point_layer);
379     trace_assert(undo_history);
380     trace_assert(a < point_layer->positions.count);
381     trace_assert(b < point_layer->positions.count);
382
383     dynarray_swap(&point_layer->positions, a, b);
384     dynarray_swap(&point_layer->colors, a, b);
385     dynarray_swap(&point_layer->ids, a, b);
386
387     POINT_UNDO_PUSH(
388         undo_history,
389         create_point_undo_swap_context(point_layer, a, b));
390 }
391
392 static
393 void point_layer_delete_nth_element(PointLayer *point_layer,
394                                     size_t i,
395                                     UndoHistory *undo_history)
396 {
397     trace_assert(point_layer);
398
399     POINT_UNDO_PUSH(
400         undo_history,
401         create_point_undo_context(
402             point_layer,
403             POINT_UNDO_DELETE));
404
405     dynarray_delete_at(&point_layer->positions, i);
406     dynarray_delete_at(&point_layer->colors, i);
407     dynarray_delete_at(&point_layer->ids, i);
408 }
409
410 static
411 int point_layer_idle_event(PointLayer *point_layer,
412                            const SDL_Event *event,
413                            const Camera *camera,
414                            UndoHistory *undo_history)
415 {
416     trace_assert(point_layer);
417     trace_assert(event);
418     trace_assert(camera);
419
420     int selected = 0;
421     if (color_picker_event(
422             &point_layer->color_picker,
423             event,
424             camera,
425             &selected) < 0) {
426         return -1;
427     }
428
429     if (selected) {
430         if (point_layer->selection >= 0) {
431             point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
432             point_layer->state = POINT_LAYER_RECOLOR;
433         }
434         return 0;
435     }
436
437     switch (event->type) {
438     case SDL_MOUSEBUTTONDOWN: {
439         switch (event->button.button) {
440         case SDL_BUTTON_LEFT: {
441             const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
442
443             point_layer->selection = point_layer_element_at(
444                 point_layer, position);
445
446             if (point_layer->selection < 0) {
447                 point_layer_add_element(
448                     point_layer,
449                     position,
450                     color_picker_rgba(&point_layer->color_picker),
451                     undo_history);
452             } else {
453                 Color *colors = (Color*)point_layer->colors.data;
454                 Vec2f *positions = (Vec2f*)point_layer->positions.data;
455
456                 point_layer->state = POINT_LAYER_MOVE;
457                 point_layer->color_picker =
458                     create_color_picker_from_rgba(colors[point_layer->selection]);
459                 point_layer->inter_position = positions[point_layer->selection];
460             }
461         } break;
462         }
463     } break;
464
465     case SDL_KEYDOWN: {
466         switch (event->key.keysym.sym) {
467         case SDLK_UP: {
468             if ((event->key.keysym.mod & KMOD_SHIFT)
469                 && (point_layer->selection >= 0)
470                 && ((size_t)(point_layer->selection + 1) < point_layer->positions.count)) {
471                 point_layer_swap_elements(
472                     point_layer,
473                     (size_t) point_layer->selection,
474                     (size_t) point_layer->selection + 1,
475                     undo_history);
476                 point_layer->selection++;
477             }
478         } break;
479
480         case SDLK_DOWN: {
481             if ((event->key.keysym.mod & KMOD_SHIFT)
482                 && (point_layer->selection > 0)
483                 && ((size_t) point_layer->selection < point_layer->positions.count)) {
484                 point_layer_swap_elements(
485                     point_layer,
486                     (size_t) point_layer->selection,
487                     (size_t) point_layer->selection - 1,
488                     undo_history);
489                 point_layer->selection--;
490             }
491         } break;
492
493         case SDLK_DELETE: {
494             if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
495                 point_layer_delete_nth_element(
496                     point_layer,
497                     (size_t)point_layer->selection,
498                     undo_history);
499                 point_layer->selection = -1;
500             }
501         } break;
502
503         case SDLK_F2: {
504             if (point_layer->selection >= 0) {
505                 char *ids = (char*)point_layer->ids.data;
506                 point_layer->state = POINT_LAYER_EDIT_ID;
507                 edit_field_replace(
508                     point_layer->edit_field,
509                     ids + ID_MAX_SIZE * point_layer->selection);
510                 SDL_StartTextInput();
511             }
512         } break;
513
514         case SDLK_c: {
515             if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
516                 point_clipboard = 1;
517                 dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
518             }
519         } break;
520
521         case SDLK_v: {
522             if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
523                 int x, y;
524                 SDL_GetMouseState(&x, &y);
525                 Vec2f position = camera_map_screen(camera, x, y);
526
527                 point_layer_add_element(
528                     point_layer,
529                     position,
530                     point_clipboard_color,
531                     undo_history);
532             }
533         } break;
534         }
535     } break;
536     }
537
538     return 0;
539 }
540
541 static
542 int point_layer_edit_id_event(PointLayer *point_layer,
543                               const SDL_Event *event,
544                               const Camera *camera,
545                               UndoHistory *undo_history)
546 {
547     trace_assert(point_layer);
548     trace_assert(event);
549     trace_assert(camera);
550
551     switch (event->type) {
552     case SDL_KEYDOWN: {
553         switch(event->key.keysym.sym) {
554         case SDLK_RETURN: {
555             POINT_UNDO_PUSH(
556                 undo_history,
557                 create_point_undo_context(
558                     point_layer,
559                     POINT_UNDO_UPDATE));
560
561             char *id = dynarray_pointer_at(&point_layer->ids, (size_t) point_layer->selection);
562             const char *text = edit_field_as_text(point_layer->edit_field);
563             size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
564             memcpy(id, text, n);
565             memset(id + n, 0, ID_MAX_SIZE - n);
566
567             point_layer->state = POINT_LAYER_IDLE;
568             SDL_StopTextInput();
569             return 0;
570         } break;
571
572         case SDLK_ESCAPE: {
573             point_layer->state = POINT_LAYER_IDLE;
574             SDL_StopTextInput();
575             return 0;
576         } break;
577         }
578     } break;
579     }
580
581     return edit_field_event(point_layer->edit_field, event);
582 }
583
584 static
585 int point_layer_move_event(PointLayer *point_layer,
586                            const SDL_Event *event,
587                            const Camera *camera,
588                            UndoHistory *undo_history)
589 {
590     trace_assert(point_layer);
591     trace_assert(event);
592     trace_assert(camera);
593     trace_assert(point_layer->selection >= 0);
594
595     Vec2f *positions = (Vec2f*)point_layer->positions.data;
596
597     switch (event->type) {
598     case SDL_MOUSEBUTTONUP: {
599         switch (event->button.button) {
600         case SDL_BUTTON_LEFT: {
601             point_layer->state = POINT_LAYER_IDLE;
602
603             const float distance = vec_length(
604                 vec_sub(point_layer->inter_position,
605                         positions[point_layer->selection]));
606
607             if (distance > 1e-6) {
608                 POINT_UNDO_PUSH(
609                     undo_history,
610                     create_point_undo_context(
611                         point_layer,
612                         POINT_UNDO_UPDATE));
613
614                 dynarray_replace_at(
615                     &point_layer->positions,
616                     (size_t) point_layer->selection,
617                     &point_layer->inter_position);
618             }
619         } break;
620         }
621     } break;
622
623     case SDL_MOUSEMOTION: {
624         const Uint8 *state = SDL_GetKeyboardState(NULL);
625         const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
626         const Vec2f point_pos = positions[point_layer->selection];
627
628         if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
629             point_layer->inter_position = mouse_pos;
630         } else {
631             const float dx = fabsf(point_pos.x - mouse_pos.x);
632             const float dy = fabsf(point_pos.y - mouse_pos.y);
633
634             if (dx > dy) {
635                 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
636             } else {
637                 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
638             }
639         }
640     } break;
641     }
642
643     return 0;
644 }
645
646 static
647 int point_layer_recolor_event(PointLayer *point_layer,
648                               const SDL_Event *event,
649                               const Camera *camera,
650                               UndoHistory *undo_history)
651 {
652     trace_assert(point_layer);
653     trace_assert(event);
654     trace_assert(camera);
655     trace_assert(undo_history);
656     trace_assert(point_layer->selection >= 0);
657
658     int selected = 0;
659     if (color_picker_event(
660             &point_layer->color_picker,
661             event,
662             camera,
663             &selected) < 0) {
664         return -1;
665     }
666
667     if (selected) {
668         point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
669
670         if (!color_picker_drag(&point_layer->color_picker)) {
671             POINT_UNDO_PUSH(
672                 undo_history,
673                 create_point_undo_context(
674                     point_layer,
675                     POINT_UNDO_UPDATE));
676
677             dynarray_replace_at(
678                 &point_layer->colors,
679                 (size_t) point_layer->selection,
680                 &point_layer->inter_color);
681
682             point_layer->state = POINT_LAYER_IDLE;
683         }
684     }
685
686
687     return 0;
688 }
689
690 int point_layer_event(PointLayer *point_layer,
691                       const SDL_Event *event,
692                       const Camera *camera,
693                       UndoHistory *undo_history)
694 {
695     trace_assert(point_layer);
696     trace_assert(event);
697     trace_assert(camera);
698     trace_assert(undo_history);
699
700     switch (point_layer->state) {
701     case POINT_LAYER_IDLE:
702         return point_layer_idle_event(point_layer, event, camera, undo_history);
703
704     case POINT_LAYER_EDIT_ID:
705         return point_layer_edit_id_event(point_layer, event, camera, undo_history);
706
707     case POINT_LAYER_MOVE:
708         return point_layer_move_event(point_layer, event, camera, undo_history);
709
710     case POINT_LAYER_RECOLOR:
711         return point_layer_recolor_event(point_layer, event, camera, undo_history);
712     }
713
714     return 0;
715 }
716
717 size_t point_layer_count(const PointLayer *point_layer)
718 {
719     trace_assert(point_layer);
720     return point_layer->positions.count;
721 }
722
723 const Vec2f *point_layer_positions(const PointLayer *point_layer)
724 {
725     trace_assert(point_layer);
726     return (const Vec2f *)point_layer->positions.data;
727 }
728
729 const Color *point_layer_colors(const PointLayer *point_layer)
730 {
731     trace_assert(point_layer);
732     return (const Color *)point_layer->colors.data;
733 }
734
735 const char *point_layer_ids(const PointLayer *point_layer)
736 {
737     trace_assert(point_layer);
738     return (const char *)point_layer->ids.data;
739 }
740
741 int point_layer_dump_stream(const PointLayer *point_layer,
742                             FILE *filedump)
743 {
744     trace_assert(point_layer);
745     trace_assert(filedump);
746
747     size_t n = point_layer->ids.count;
748     char *ids = (char *) point_layer->ids.data;
749     Vec2f *positions = (Vec2f *) point_layer->positions.data;
750     Color *colors = (Color *) point_layer->colors.data;
751
752     fprintf(filedump, "%zd\n", n);
753     for (size_t i = 0; i < n; ++i) {
754         fprintf(filedump, "%s %f %f ",
755                 ids + ID_MAX_SIZE * i,
756                 positions[i].x, positions[i].y);
757         color_hex_to_stream(colors[i], filedump);
758         fprintf(filedump, "\n");
759     }
760
761     return 0;
762 }