]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
Add TODO(#1013)
[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     Action action =  {
261         .revert = point_layer_pop_element,
262         .layer = point_layer
263     };
264     undo_history_push(undo_history, action);
265
266     return 0;
267 }
268
269 typedef struct {
270     Point position;
271     Color color;
272     char id[ID_MAX_SIZE];
273     size_t index;
274 } DeleteContext;
275
276 static
277 void point_layer_revert_delete(void *layer, Context context)
278 {
279     trace_assert(layer);
280     PointLayer *point_layer = layer;
281
282     trace_assert(sizeof(DeleteContext) <= CONTEXT_SIZE);
283     DeleteContext *delete_context = (DeleteContext *)context.data;
284
285     dynarray_insert_before(point_layer->positions, delete_context->index, &delete_context->position);
286     dynarray_insert_before(point_layer->colors, delete_context->index, &delete_context->color);
287     dynarray_insert_before(point_layer->ids, delete_context->index, delete_context->id);
288 }
289
290 static
291 void point_layer_delete_nth_element(PointLayer *point_layer,
292                                     size_t i,
293                                     UndoHistory *undo_history)
294 {
295     trace_assert(point_layer);
296
297     Action action = {
298         .revert = point_layer_revert_delete,
299         .layer = point_layer
300     };
301
302     trace_assert(sizeof(DeleteContext) <= CONTEXT_SIZE);
303     DeleteContext *delete_context = (DeleteContext *)action.context.data;
304
305     Point *positions = dynarray_data(point_layer->positions);
306     Color *colors = dynarray_data(point_layer->colors);
307     char *ids = dynarray_data(point_layer->ids);
308
309     delete_context->position = positions[i];
310     delete_context->color = colors[i];
311     memcpy(
312         delete_context->id,
313         ids + i * ID_MAX_SIZE,
314         ID_MAX_SIZE);
315     delete_context->index = i;
316
317     undo_history_push(undo_history, action);
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             &selected) < 0) {
362         return -1;
363     }
364
365     if (selected) {
366         if (point_layer->selected >= 0) {
367             Color *colors = dynarray_data(point_layer->colors);
368
369             if (!color_picker_drag(&point_layer->color_picker)) {
370                 Action action = {
371                     .layer = point_layer,
372                     .revert = point_layer_revert_color
373                 };
374
375                 *((ColorContext*)action.context.data) = (ColorContext) {
376                     .index = (size_t) point_layer->selected,
377                     .color = point_layer->prev_color
378                 };
379
380                 undo_history_push(undo_history, action);
381
382                 point_layer->prev_color =
383                     color_picker_rgba(&point_layer->color_picker);
384             }
385
386             colors[point_layer->selected] =
387                 color_picker_rgba(&point_layer->color_picker);
388         }
389
390         return 0;
391     }
392
393     switch (event->type) {
394     case SDL_MOUSEBUTTONDOWN: {
395         switch (event->button.button) {
396         case SDL_BUTTON_LEFT: {
397             const Point position = camera_map_screen(camera, event->button.x, event->button.y);
398
399             point_layer->selected = point_layer_element_at(
400                 point_layer, position);
401
402             if (point_layer->selected < 0) {
403                 point_layer_add_element(
404                     point_layer,
405                     position,
406                     color_picker_rgba(&point_layer->color_picker),
407                     undo_history);
408             } else {
409                 Color *colors = dynarray_data(point_layer->colors);
410                 Point *positions = dynarray_data(point_layer->positions);
411
412                 point_layer->state = POINT_LAYER_MOVE;
413                 point_layer->color_picker =
414                     create_color_picker_from_rgba(colors[point_layer->selected]);
415
416                 point_layer->prev_color = colors[point_layer->selected];
417                 point_layer->prev_position = positions[point_layer->selected];
418             }
419         } break;
420         }
421     } break;
422
423     case SDL_KEYDOWN: {
424         switch (event->key.keysym.sym) {
425         case SDLK_DELETE: {
426             if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
427                 point_layer_delete_nth_element(
428                     point_layer,
429                     (size_t)point_layer->selected,
430                     undo_history);
431                 point_layer->selected = -1;
432             }
433         } break;
434
435         case SDLK_F2: {
436             if (point_layer->selected >= 0) {
437                 char *ids = dynarray_data(point_layer->ids);
438                 point_layer->state = POINT_LAYER_EDIT_ID;
439                 edit_field_replace(
440                     point_layer->edit_field,
441                     ids + ID_MAX_SIZE * point_layer->selected);
442                 SDL_StartTextInput();
443             }
444         } break;
445         }
446     } break;
447     }
448
449     return 0;
450 }
451
452 typedef struct {
453     size_t index;
454     char id[ID_MAX_SIZE];
455 } RenameContext;
456
457 static
458 void point_layer_revert_rename(void *layer,
459                                Context context)
460 {
461     trace_assert(layer);
462     PointLayer *point_layer = layer;
463
464     ASSERT_CONTEXT_SIZE(RenameContext);
465     RenameContext *rename_context = (RenameContext *)context.data;
466
467     trace_assert(rename_context->index < dynarray_count(point_layer->ids));
468
469     char *ids = dynarray_data(point_layer->ids);
470     memcpy(
471         ids + rename_context->index * ID_MAX_SIZE,
472         rename_context->id,
473         ID_MAX_SIZE);
474 }
475
476 static
477 int point_layer_edit_id_event(PointLayer *point_layer,
478                               const SDL_Event *event,
479                               const Camera *camera,
480                               UndoHistory *undo_history)
481 {
482     trace_assert(point_layer);
483     trace_assert(event);
484     trace_assert(camera);
485
486     switch (event->type) {
487     case SDL_KEYDOWN: {
488         switch(event->key.keysym.sym) {
489         case SDLK_RETURN: {
490             char *ids = dynarray_data(point_layer->ids);
491             const char *text = edit_field_as_text(point_layer->edit_field);
492
493             Action action = {
494                 .revert = point_layer_revert_rename,
495                 .layer = point_layer
496             };
497
498             ASSERT_CONTEXT_SIZE(RenameContext);
499             RenameContext *rename_context = (RenameContext *)action.context.data;
500
501             memcpy(
502                 rename_context->id,
503                 ids + point_layer->selected * ID_MAX_SIZE,
504                 ID_MAX_SIZE);
505             rename_context->index = (size_t) point_layer->selected;
506
507             undo_history_push(undo_history, action);
508
509             size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
510             memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
511             *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
512
513             point_layer->state = POINT_LAYER_IDLE;
514             SDL_StopTextInput();
515             return 0;
516         } break;
517
518         case SDLK_ESCAPE: {
519             point_layer->state = POINT_LAYER_IDLE;
520             SDL_StopTextInput();
521             return 0;
522         } break;
523         }
524     } break;
525     }
526
527     return edit_field_event(point_layer->edit_field, event);
528 }
529
530 typedef struct {
531     size_t index;
532     Point position;
533 } MoveContext;
534
535 static
536 void point_layer_revert_move(void *layer, Context context)
537 {
538     trace_assert(layer);
539     PointLayer *point_layer = layer;
540
541     ASSERT_CONTEXT_SIZE(MoveContext);
542     MoveContext *move_context = (MoveContext *)context.data;
543
544     trace_assert(move_context->index < dynarray_count(point_layer->positions));
545     Point *positions = dynarray_data(point_layer->positions);
546     positions[move_context->index] = move_context->position;
547 }
548
549 static
550 int point_layer_move_event(PointLayer *point_layer,
551                            const SDL_Event *event,
552                            const Camera *camera,
553                            UndoHistory *undo_history)
554 {
555     trace_assert(point_layer);
556     trace_assert(event);
557     trace_assert(camera);
558     trace_assert(point_layer->selected >= 0);
559
560     switch (event->type) {
561     case SDL_MOUSEBUTTONUP: {
562         switch (event->button.button) {
563         case SDL_BUTTON_LEFT: {
564             point_layer->state = POINT_LAYER_IDLE;
565
566             Action action = {
567                 .revert = point_layer_revert_move,
568                 .layer = point_layer
569             };
570
571             MoveContext *context = (MoveContext *)action.context.data;
572             ASSERT_CONTEXT_SIZE(MoveContext);
573
574             context->index = (size_t) point_layer->selected;
575             context->position = point_layer->prev_position;
576
577             // TODO: just click (without moving) on the point creates an undo history entry
578             undo_history_push(undo_history, action);
579         } break;
580         }
581     } break;
582
583     case SDL_MOUSEMOTION: {
584         Point *positions = dynarray_data(point_layer->positions);
585         positions[point_layer->selected] =
586             camera_map_screen(camera, event->motion.x, event->motion.y);
587     } break;
588     }
589
590     return 0;
591 }
592
593 int point_layer_event(PointLayer *point_layer,
594                       const SDL_Event *event,
595                       const Camera *camera,
596                       UndoHistory *undo_history)
597 {
598     trace_assert(point_layer);
599     trace_assert(event);
600     trace_assert(camera);
601     trace_assert(undo_history);
602
603     switch (point_layer->state) {
604     case POINT_LAYER_IDLE:
605         return point_layer_idle_event(point_layer, event, camera, undo_history);
606
607     case POINT_LAYER_EDIT_ID:
608         return point_layer_edit_id_event(point_layer, event, camera, undo_history);
609
610     case POINT_LAYER_MOVE:
611         return point_layer_move_event(point_layer, event, camera, undo_history);
612     }
613
614     return 0;
615 }
616
617 size_t point_layer_count(const PointLayer *point_layer)
618 {
619     trace_assert(point_layer);
620     return dynarray_count(point_layer->positions);
621 }
622
623 const Point *point_layer_positions(const PointLayer *point_layer)
624 {
625     trace_assert(point_layer);
626     return dynarray_data(point_layer->positions);
627 }
628
629 const Color *point_layer_colors(const PointLayer *point_layer)
630 {
631     trace_assert(point_layer);
632     return dynarray_data(point_layer->colors);
633 }
634
635 const char *point_layer_ids(const PointLayer *point_layer)
636 {
637     trace_assert(point_layer);
638     return dynarray_data(point_layer->ids);
639 }
640
641 int point_layer_dump_stream(const PointLayer *point_layer,
642                             FILE *filedump)
643 {
644     trace_assert(point_layer);
645     trace_assert(filedump);
646
647     size_t n = dynarray_count(point_layer->ids);
648     char *ids = dynarray_data(point_layer->ids);
649     Point *positions = dynarray_data(point_layer->positions);
650     Color *colors = dynarray_data(point_layer->colors);
651
652     fprintf(filedump, "%zd\n", n);
653     for (size_t i = 0; i < n; ++i) {
654         fprintf(filedump, "%s %f %f ",
655                 ids + ID_MAX_SIZE * i,
656                 positions[i].x, positions[i].y);
657         color_hex_to_stream(colors[i], filedump);
658         fprintf(filedump, "\n");
659     }
660
661     return 0;
662 }