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