]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
Add TODO(#1034)
[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 "./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 // TODO(#1002): PointLayer does not fully support UndoHistory
24
25 typedef enum {
26     POINT_LAYER_IDLE = 0,
27     POINT_LAYER_EDIT_ID,
28     POINT_LAYER_MOVE
29 } PointLayerState;
30
31 struct PointLayer
32 {
33     Lt *lt;
34     PointLayerState state;
35     Dynarray/*<Point>*/ *positions;
36     Dynarray/*<Color>*/ *colors;
37     Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
38     Edit_field *edit_field;
39     int selected;
40     ColorPicker color_picker;
41     Color prev_color;
42     Point prev_position;
43 };
44
45 LayerPtr point_layer_as_layer(PointLayer *point_layer)
46 {
47     LayerPtr layer = {
48         .type = LAYER_POINT,
49         .ptr = point_layer
50     };
51     return layer;
52 }
53
54 PointLayer *create_point_layer(void)
55 {
56     Lt *lt = create_lt();
57
58     PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
59     if (point_layer == NULL) {
60         RETURN_LT(lt, NULL);
61     }
62     point_layer->lt = lt;
63
64     point_layer->state = POINT_LAYER_IDLE;
65
66     point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
67     if (point_layer->positions == NULL) {
68         RETURN_LT(lt, NULL);
69     }
70
71     point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
72     if (point_layer->colors == NULL) {
73         RETURN_LT(lt, NULL);
74     }
75
76     point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
77     if (point_layer->ids == NULL) {
78         RETURN_LT(lt, NULL);
79     }
80
81     point_layer->edit_field = PUSH_LT(
82         lt,
83         create_edit_field(
84             POINT_LAYER_ID_TEXT_SIZE,
85             POINT_LAYER_ID_TEXT_COLOR),
86         destroy_edit_field);
87     if (point_layer->edit_field == NULL) {
88         RETURN_LT(lt, NULL);
89     }
90
91     return point_layer;
92 }
93
94 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
95 {
96     trace_assert(line_stream);
97
98     PointLayer *point_layer = create_point_layer();
99
100     size_t count = 0;
101     if (sscanf(
102             line_stream_next(line_stream),
103             "%zu",
104             &count) == EOF) {
105         log_fail("Could not read amount of points");
106         RETURN_LT(point_layer->lt, NULL);
107     }
108
109     char color_name[7];
110     char id[ID_MAX_SIZE];
111     float x, y;
112     for (size_t i = 0; i < count; ++i) {
113         if (sscanf(
114                 line_stream_next(line_stream),
115                 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
116                 id, &x, &y, color_name) < 0) {
117             log_fail("Could not read %dth goal\n", i);
118             RETURN_LT(point_layer->lt, NULL);
119         }
120         const Color color = hexstr(color_name);
121         const Point point = vec(x, y);
122
123         dynarray_push(point_layer->colors, &color);
124         dynarray_push(point_layer->positions, &point);
125         dynarray_push(point_layer->ids, id);
126     }
127
128     point_layer->selected = -1;
129
130     point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
131     point_layer->prev_color = COLOR_RED;
132
133     return point_layer;
134 }
135
136 void destroy_point_layer(PointLayer *point_layer)
137 {
138     trace_assert(point_layer);
139     RETURN_LT0(point_layer->lt);
140 }
141
142 int point_layer_render(const PointLayer *point_layer,
143                        Camera *camera,
144                        int active)
145 {
146     trace_assert(point_layer);
147     trace_assert(camera);
148
149     const int n = (int) dynarray_count(point_layer->positions);
150     Point *positions = dynarray_data(point_layer->positions);
151     Color *colors = dynarray_data(point_layer->colors);
152     char *ids = dynarray_data(point_layer->ids);
153
154     for (int i = 0; i < n; ++i) {
155         const Triangle t = triangle_mat3x3_product(
156             equilateral_triangle(),
157             mat3x3_product(
158                 trans_mat(positions[i].x, positions[i].y),
159                 scale_mat(POINT_LAYER_ELEMENT_RADIUS)));
160
161         const Color color = color_scale(
162             colors[i],
163             rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
164
165         if (i == point_layer->selected) {
166             const Triangle t0 = triangle_mat3x3_product(
167                 equilateral_triangle(),
168                 mat3x3_product(
169                     trans_mat(positions[i].x, positions[i].y),
170                     scale_mat(15.0f)));
171
172             if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) {
173                 return -1;
174             }
175
176             if (point_layer->state != POINT_LAYER_EDIT_ID &&
177                 camera_render_text(
178                     camera,
179                     ids + ID_MAX_SIZE * i,
180                     POINT_LAYER_ID_TEXT_SIZE,
181                     POINT_LAYER_ID_TEXT_COLOR,
182                     positions[i]) < 0) {
183                 return -1;
184             }
185         }
186
187         if (camera_fill_triangle(camera, t, color) < 0) {
188             return -1;
189         }
190
191     }
192
193     if (point_layer->state == POINT_LAYER_EDIT_ID) {
194         if (edit_field_render_world(
195                 point_layer->edit_field,
196                 camera,
197                 positions[point_layer->selected]) < 0) {
198             return -1;
199         }
200     }
201
202     if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
203         return -1;
204     }
205
206
207     return 0;
208 }
209
210 static
211 int point_layer_element_at(const PointLayer *point_layer,
212                            Point position)
213 {
214     trace_assert(point_layer);
215
216     int n = (int) dynarray_count(point_layer->positions);
217     Point *positions = dynarray_data(point_layer->positions);
218
219     for (int i = 0; i < n; ++i) {
220         if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
221             return i;
222         }
223     }
224
225     return -1;
226 }
227
228 static
229 void point_layer_pop_element(void *layer, Context context)
230 {
231     trace_assert(layer);
232     (void) context;
233
234     PointLayer *point_layer = layer;
235
236     dynarray_pop(point_layer->positions, NULL);
237     dynarray_pop(point_layer->colors, NULL);
238     dynarray_pop(point_layer->ids, NULL);
239 }
240
241 static
242 int point_layer_add_element(PointLayer *point_layer,
243                             Point position,
244                             Color color,
245                             UndoHistory *undo_history)
246 {
247     trace_assert(point_layer);
248     trace_assert(undo_history);
249
250     char id[ID_MAX_SIZE];
251     for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
252         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
253     }
254     id[ID_MAX_SIZE - 1] = '\0';
255
256     dynarray_push(point_layer->positions, &position);
257     dynarray_push(point_layer->colors, &color);
258     dynarray_push(point_layer->ids, id);
259
260     undo_history_push(
261         undo_history,
262         create_action(
263             point_layer,
264             point_layer_pop_element,
265             0, 0));
266
267     return 0;
268 }
269
270 typedef struct {
271     Point position;
272     Color color;
273     char id[ID_MAX_SIZE];
274     size_t index;
275 } DeleteContext;
276
277 static
278 DeleteContext create_delete_context(const PointLayer *point_layer,
279                                     size_t i)
280 {
281     DeleteContext delete_context = {
282         .position = *((Point *)dynarray_pointer_at(point_layer->positions, i)),
283         .color = *((Color *)dynarray_pointer_at(point_layer->colors, i)),
284         .index = i
285     };
286     memcpy(delete_context.id, dynarray_pointer_at(point_layer->ids, i), ID_MAX_SIZE);
287     return delete_context;
288 }
289
290 static
291 void point_layer_revert_delete(void *layer, Context context)
292 {
293     trace_assert(layer);
294     PointLayer *point_layer = layer;
295
296     trace_assert(sizeof(DeleteContext) <= CONTEXT_SIZE);
297     DeleteContext *delete_context = (DeleteContext *)context.data;
298
299     dynarray_insert_before(point_layer->positions, delete_context->index, &delete_context->position);
300     dynarray_insert_before(point_layer->colors, delete_context->index, &delete_context->color);
301     dynarray_insert_before(point_layer->ids, delete_context->index, delete_context->id);
302 }
303
304 static
305 void point_layer_delete_nth_element(PointLayer *point_layer,
306                                     size_t i,
307                                     UndoHistory *undo_history)
308 {
309     trace_assert(point_layer);
310
311     DeleteContext context = create_delete_context(point_layer, i);
312     undo_history_push(
313         undo_history,
314         create_action(
315             point_layer,
316             point_layer_revert_delete,
317             &context, sizeof(context)));
318
319     dynarray_delete_at(point_layer->positions, i);
320     dynarray_delete_at(point_layer->colors, i);
321     dynarray_delete_at(point_layer->ids, i);
322 }
323
324 typedef struct {
325     size_t index;
326     Color color;
327 } ColorContext;
328
329 static
330 void point_layer_revert_color(void *layer, Context context)
331 {
332     log_info("point_layer_revert_color\n");
333
334     trace_assert(layer);
335     PointLayer *point_layer = layer;
336
337     trace_assert(sizeof(ColorContext) <= CONTEXT_SIZE);
338     ColorContext *color_context = (ColorContext*)context.data;
339
340     const size_t n = dynarray_count(point_layer->colors);
341     Color *colors = dynarray_data(point_layer->colors);
342     trace_assert(color_context->index < n);
343
344     colors[color_context->index] = color_context->color;
345 }
346
347 static
348 int point_layer_idle_event(PointLayer *point_layer,
349                            const SDL_Event *event,
350                            const Camera *camera,
351                            UndoHistory *undo_history)
352 {
353     trace_assert(point_layer);
354     trace_assert(event);
355     trace_assert(camera);
356
357     int selected = 0;
358     if (color_picker_event(
359             &point_layer->color_picker,
360             event,
361             camera,
362             &selected) < 0) {
363         return -1;
364     }
365
366     if (selected) {
367         if (point_layer->selected >= 0) {
368             Color *colors = dynarray_data(point_layer->colors);
369
370             if (!color_picker_drag(&point_layer->color_picker)) {
371                 ColorContext context = {
372                     .index = (size_t) point_layer->selected,
373                     .color = point_layer->prev_color
374                 };
375
376                 undo_history_push(
377                     undo_history,
378                     create_action(
379                         point_layer,
380                         point_layer_revert_color,
381                         &context, sizeof(context)));
382
383                 point_layer->prev_color =
384                     color_picker_rgba(&point_layer->color_picker);
385             }
386
387             colors[point_layer->selected] =
388                 color_picker_rgba(&point_layer->color_picker);
389         }
390
391         return 0;
392     }
393
394     switch (event->type) {
395     case SDL_MOUSEBUTTONDOWN: {
396         switch (event->button.button) {
397         case SDL_BUTTON_LEFT: {
398             const Point position = camera_map_screen(camera, event->button.x, event->button.y);
399
400             point_layer->selected = point_layer_element_at(
401                 point_layer, position);
402
403             if (point_layer->selected < 0) {
404                 point_layer_add_element(
405                     point_layer,
406                     position,
407                     color_picker_rgba(&point_layer->color_picker),
408                     undo_history);
409             } else {
410                 Color *colors = dynarray_data(point_layer->colors);
411                 Point *positions = dynarray_data(point_layer->positions);
412
413                 point_layer->state = POINT_LAYER_MOVE;
414                 point_layer->color_picker =
415                     create_color_picker_from_rgba(colors[point_layer->selected]);
416
417                 point_layer->prev_color = colors[point_layer->selected];
418                 point_layer->prev_position = positions[point_layer->selected];
419             }
420         } break;
421         }
422     } break;
423
424     case SDL_KEYDOWN: {
425         switch (event->key.keysym.sym) {
426         case SDLK_DELETE: {
427             if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
428                 point_layer_delete_nth_element(
429                     point_layer,
430                     (size_t)point_layer->selected,
431                     undo_history);
432                 point_layer->selected = -1;
433             }
434         } break;
435
436         case SDLK_F2: {
437             if (point_layer->selected >= 0) {
438                 char *ids = dynarray_data(point_layer->ids);
439                 point_layer->state = POINT_LAYER_EDIT_ID;
440                 edit_field_replace(
441                     point_layer->edit_field,
442                     ids + ID_MAX_SIZE * point_layer->selected);
443                 SDL_StartTextInput();
444             }
445         } break;
446         }
447     } break;
448     }
449
450     return 0;
451 }
452
453 typedef struct {
454     size_t index;
455     char id[ID_MAX_SIZE];
456 } RenameContext;
457
458 static
459 RenameContext create_rename_context(PointLayer *point_layer, size_t index)
460 {
461     RenameContext context = {
462         .index = index
463     };
464
465     memcpy(context.id, dynarray_pointer_at(point_layer->ids, index), ID_MAX_SIZE);
466
467     return context;
468 }
469
470 static
471 void point_layer_revert_rename(void *layer,
472                                Context context)
473 {
474     trace_assert(layer);
475     PointLayer *point_layer = layer;
476
477     ASSERT_CONTEXT_SIZE(RenameContext);
478     RenameContext *rename_context = (RenameContext *)context.data;
479
480     trace_assert(rename_context->index < dynarray_count(point_layer->ids));
481
482     char *ids = dynarray_data(point_layer->ids);
483     memcpy(
484         ids + rename_context->index * ID_MAX_SIZE,
485         rename_context->id,
486         ID_MAX_SIZE);
487 }
488
489 static
490 int point_layer_edit_id_event(PointLayer *point_layer,
491                               const SDL_Event *event,
492                               const Camera *camera,
493                               UndoHistory *undo_history)
494 {
495     trace_assert(point_layer);
496     trace_assert(event);
497     trace_assert(camera);
498
499     switch (event->type) {
500     case SDL_KEYDOWN: {
501         switch(event->key.keysym.sym) {
502         case SDLK_RETURN: {
503             RenameContext rename_context = create_rename_context(
504                 point_layer,
505                 (size_t) point_layer->selected);
506
507             undo_history_push(
508                 undo_history,
509                 create_action(
510                     point_layer,
511                     point_layer_revert_rename,
512                     &rename_context,
513                     sizeof(rename_context)));
514
515             char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
516             const char *text = edit_field_as_text(point_layer->edit_field);
517             memset(id, 0, ID_MAX_SIZE);
518             memcpy(id, text, min_size_t(strlen(text), ID_MAX_SIZE - 1));
519
520             point_layer->state = POINT_LAYER_IDLE;
521             SDL_StopTextInput();
522             return 0;
523         } break;
524
525         case SDLK_ESCAPE: {
526             point_layer->state = POINT_LAYER_IDLE;
527             SDL_StopTextInput();
528             return 0;
529         } break;
530         }
531     } break;
532     }
533
534     return edit_field_event(point_layer->edit_field, event);
535 }
536
537 typedef struct {
538     size_t index;
539     Point position;
540 } MoveContext;
541
542 static
543 void point_layer_revert_move(void *layer, Context context)
544 {
545     trace_assert(layer);
546     PointLayer *point_layer = layer;
547
548     ASSERT_CONTEXT_SIZE(MoveContext);
549     MoveContext *move_context = (MoveContext *)context.data;
550
551     trace_assert(move_context->index < dynarray_count(point_layer->positions));
552     Point *positions = dynarray_data(point_layer->positions);
553     positions[move_context->index] = move_context->position;
554 }
555
556 static
557 int point_layer_move_event(PointLayer *point_layer,
558                            const SDL_Event *event,
559                            const Camera *camera,
560                            UndoHistory *undo_history)
561 {
562     trace_assert(point_layer);
563     trace_assert(event);
564     trace_assert(camera);
565     trace_assert(point_layer->selected >= 0);
566
567     switch (event->type) {
568     case SDL_MOUSEBUTTONUP: {
569         switch (event->button.button) {
570         case SDL_BUTTON_LEFT: {
571             point_layer->state = POINT_LAYER_IDLE;
572
573             MoveContext context = {
574                 .index = (size_t) point_layer->selected,
575                 .position = point_layer->prev_position
576             };
577
578             // TODO(#1014): just click (without moving) on the point creates an undo history entry
579             undo_history_push(
580                 undo_history,
581                 create_action(
582                     point_layer,
583                     point_layer_revert_move,
584                     &context,
585                     sizeof(context)));
586         } break;
587         }
588     } break;
589
590     case SDL_MOUSEMOTION: {
591         Point *positions = dynarray_data(point_layer->positions);
592         positions[point_layer->selected] =
593             camera_map_screen(camera, event->motion.x, event->motion.y);
594     } break;
595     }
596
597     return 0;
598 }
599
600 int point_layer_event(PointLayer *point_layer,
601                       const SDL_Event *event,
602                       const Camera *camera,
603                       UndoHistory *undo_history)
604 {
605     trace_assert(point_layer);
606     trace_assert(event);
607     trace_assert(camera);
608     trace_assert(undo_history);
609
610     switch (point_layer->state) {
611     case POINT_LAYER_IDLE:
612         return point_layer_idle_event(point_layer, event, camera, undo_history);
613
614     case POINT_LAYER_EDIT_ID:
615         return point_layer_edit_id_event(point_layer, event, camera, undo_history);
616
617     case POINT_LAYER_MOVE:
618         return point_layer_move_event(point_layer, event, camera, undo_history);
619     }
620
621     return 0;
622 }
623
624 size_t point_layer_count(const PointLayer *point_layer)
625 {
626     trace_assert(point_layer);
627     return dynarray_count(point_layer->positions);
628 }
629
630 const Point *point_layer_positions(const PointLayer *point_layer)
631 {
632     trace_assert(point_layer);
633     return dynarray_data(point_layer->positions);
634 }
635
636 const Color *point_layer_colors(const PointLayer *point_layer)
637 {
638     trace_assert(point_layer);
639     return dynarray_data(point_layer->colors);
640 }
641
642 const char *point_layer_ids(const PointLayer *point_layer)
643 {
644     trace_assert(point_layer);
645     return dynarray_data(point_layer->ids);
646 }
647
648 int point_layer_dump_stream(const PointLayer *point_layer,
649                             FILE *filedump)
650 {
651     trace_assert(point_layer);
652     trace_assert(filedump);
653
654     size_t n = dynarray_count(point_layer->ids);
655     char *ids = dynarray_data(point_layer->ids);
656     Point *positions = dynarray_data(point_layer->positions);
657     Color *colors = dynarray_data(point_layer->colors);
658
659     fprintf(filedump, "%zd\n", n);
660     for (size_t i = 0; i < n; ++i) {
661         fprintf(filedump, "%s %f %f ",
662                 ids + ID_MAX_SIZE * i,
663                 positions[i].x, positions[i].y);
664         color_hex_to_stream(colors[i], filedump);
665         fprintf(filedump, "\n");
666     }
667
668     return 0;
669 }