]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
d44957a5efed94e12bb4c22212b4f3a83f5bac29
[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
18 #define POINT_LAYER_ELEMENT_RADIUS 10.0f
19
20 typedef enum {
21     POINT_LAYER_NORMAL_STATE = 0,
22     POINT_LAYER_ID_EDITING_STATE
23 } PointLayerState;
24
25 struct PointLayer
26 {
27     Lt *lt;
28     PointLayerState state;
29     Dynarray/*<Point>*/ *points;
30     Dynarray/*<Color>*/ *colors;
31     Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
32     Edit_field *edit_field;
33     int selected;
34     ColorPicker color_picker;
35 };
36
37 LayerPtr point_layer_as_layer(PointLayer *point_layer)
38 {
39     LayerPtr layer = {
40         .type = LAYER_POINT,
41         .ptr = point_layer
42     };
43     return layer;
44 }
45
46 PointLayer *create_point_layer(void)
47 {
48     Lt *lt = create_lt();
49
50     PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
51     if (point_layer == NULL) {
52         RETURN_LT(lt, NULL);
53     }
54     point_layer->lt = lt;
55
56     point_layer->state = POINT_LAYER_NORMAL_STATE;
57
58     point_layer->points = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
59     if (point_layer->points == NULL) {
60         RETURN_LT(lt, NULL);
61     }
62
63     point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
64     if (point_layer->colors == NULL) {
65         RETURN_LT(lt, NULL);
66     }
67
68     point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
69     if (point_layer->ids == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72
73     point_layer->edit_field = PUSH_LT(
74         lt,
75         create_edit_field(
76             vec(5.0f, 5.0f),
77             rgba(0.0f, 0.0f, 0.0f, 1.0f)),
78         destroy_edit_field);
79     if (point_layer->edit_field == NULL) {
80         RETURN_LT(lt, NULL);
81     }
82
83     return point_layer;
84 }
85
86 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
87 {
88     trace_assert(line_stream);
89
90     PointLayer *point_layer = create_point_layer();
91
92     size_t count = 0;
93     if (sscanf(
94             line_stream_next(line_stream),
95             "%zu",
96             &count) == EOF) {
97         log_fail("Could not read amount of points");
98         RETURN_LT(point_layer->lt, NULL);
99     }
100
101     char color_name[7];
102     char id[ID_MAX_SIZE];
103     float x, y;
104     for (size_t i = 0; i < count; ++i) {
105         if (sscanf(
106                 line_stream_next(line_stream),
107                 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
108                 id, &x, &y, color_name) < 0) {
109             log_fail("Could not read %dth goal\n", i);
110             RETURN_LT(point_layer->lt, NULL);
111         }
112         const Color color = hexstr(color_name);
113         const Point point = vec(x, y);
114
115         dynarray_push(point_layer->colors, &color);
116         dynarray_push(point_layer->points, &point);
117         dynarray_push(point_layer->ids, id);
118     }
119
120     point_layer->selected = -1;
121
122     point_layer->color_picker.color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
123
124     return point_layer;
125 }
126
127 void destroy_point_layer(PointLayer *point_layer)
128 {
129     trace_assert(point_layer);
130     RETURN_LT0(point_layer->lt);
131 }
132
133 int point_layer_render(const PointLayer *point_layer,
134                        Camera *camera,
135                        int active)
136 {
137     trace_assert(point_layer);
138     trace_assert(camera);
139
140     const int n = (int) dynarray_count(point_layer->points);
141     Point *points = dynarray_data(point_layer->points);
142     Color *colors = dynarray_data(point_layer->colors);
143
144     for (int i = 0; i < n; ++i) {
145         const Triangle t = triangle_mat3x3_product(
146             equilateral_triangle(),
147             mat3x3_product(
148                 trans_mat(points[i].x, points[i].y),
149                 scale_mat(POINT_LAYER_ELEMENT_RADIUS)));
150
151         const Color color = color_scale(
152             colors[i],
153             rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
154
155         if (i == point_layer->selected) {
156             const Triangle t0 = triangle_mat3x3_product(
157                 equilateral_triangle(),
158                 mat3x3_product(
159                     trans_mat(points[i].x, points[i].y),
160                     scale_mat(15.0f)));
161
162             if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) {
163                 return -1;
164             }
165         }
166
167         if (camera_fill_triangle(camera, t, color) < 0) {
168             return -1;
169         }
170
171         /* TODO(#854): The ids of PointLayer are not displayed constantly */
172     }
173
174     if (point_layer->state == POINT_LAYER_ID_EDITING_STATE) {
175         /* TODO(#855): PointLayer edit field is not scaled on zoom */
176         if (edit_field_render(
177                 point_layer->edit_field,
178                 camera,
179                 camera_point(camera, points[point_layer->selected])) < 0) {
180             return -1;
181         }
182     }
183
184     if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
185         return -1;
186     }
187
188
189     return 0;
190 }
191
192
193 static int point_layer_mouse_button(PointLayer *point_layer,
194                                     const SDL_MouseButtonEvent *event,
195                                     const Camera *camera)
196 {
197     trace_assert(point_layer);
198     trace_assert(event);
199
200     if (point_layer->state == POINT_LAYER_NORMAL_STATE &&
201         event->type == SDL_MOUSEBUTTONDOWN &&
202         event->button == SDL_BUTTON_LEFT) {
203         const int n = (int) dynarray_count(point_layer->points);
204         const Point *points = dynarray_data(point_layer->points);
205         const Point point = camera_map_screen(camera, event->x, event->y);
206
207         for (int i = 0; i < n; ++i) {
208             if (vec_length(vec_sub(points[i], point)) < POINT_LAYER_ELEMENT_RADIUS) {
209                 point_layer->selected = i;
210                 return 0;
211             }
212         }
213
214         char id[ID_MAX_SIZE];
215
216         for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
217             id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
218         }
219         id[ID_MAX_SIZE - 1] = '\0';
220
221         dynarray_push(point_layer->points, &point);
222         dynarray_push(point_layer->colors, &point_layer->color_picker.color);
223         dynarray_push(point_layer->ids, id);
224     }
225
226     return 0;
227 }
228
229 static
230 int point_layer_keyboard(PointLayer *point_layer,
231                          const SDL_KeyboardEvent *key)
232 {
233     trace_assert(point_layer);
234     trace_assert(key);
235
236     switch(point_layer->state) {
237     case POINT_LAYER_NORMAL_STATE: {
238         if (key->type == SDL_KEYDOWN) {
239             switch (key->keysym.sym) {
240             case SDLK_DELETE: {
241                 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->points)) {
242                     dynarray_delete_at(point_layer->points, (size_t) point_layer->selected);
243                     dynarray_delete_at(point_layer->colors, (size_t) point_layer->selected);
244                     dynarray_delete_at(point_layer->ids, (size_t) point_layer->selected);
245                 }
246                 point_layer->selected = -1;
247             } break;
248
249             case SDLK_F2: {
250                 if (point_layer->selected >= 0) {
251                     char *ids = dynarray_data(point_layer->ids);
252                     point_layer->state = POINT_LAYER_ID_EDITING_STATE;
253                     edit_field_replace(
254                         point_layer->edit_field,
255                         ids + ID_MAX_SIZE * point_layer->selected);
256                     SDL_StartTextInput();
257                 }
258             } break;
259
260             default: {}
261             }
262         }
263     } break;
264
265     case POINT_LAYER_ID_EDITING_STATE: {
266         if (edit_field_keyboard(point_layer->edit_field, key) < 0) {
267             return -1;
268         }
269
270         if (key->type == SDL_KEYDOWN) {
271             switch(key->keysym.sym) {
272             case SDLK_RETURN: {
273                 char *ids = dynarray_data(point_layer->ids);
274                 const char *text = edit_field_as_text(point_layer->edit_field);
275                 size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
276                 memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
277                 *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
278                 point_layer->state = POINT_LAYER_NORMAL_STATE;
279                 SDL_StopTextInput();
280             } break;
281
282             case SDLK_ESCAPE: {
283                 point_layer->state = POINT_LAYER_NORMAL_STATE;
284                 SDL_StopTextInput();
285             } break;
286             }
287         }
288     } break;
289     }
290
291
292     return 0;
293 }
294
295 static
296 int point_layer_text_input(PointLayer *point_layer,
297                            const SDL_TextInputEvent *text_input)
298 {
299     trace_assert(point_layer);
300     trace_assert(text_input);
301
302     if (point_layer->state == POINT_LAYER_ID_EDITING_STATE) {
303         /* TODO(#856): Special development keybindings interfere with id editing field */
304         return edit_field_text_input(point_layer->edit_field, text_input);
305     }
306
307     return 0;
308 }
309
310 int point_layer_event(PointLayer *point_layer,
311                       const SDL_Event *event,
312                       const Camera *camera)
313 {
314     trace_assert(point_layer);
315     trace_assert(event);
316     trace_assert(camera);
317
318     int selected = 0;
319     if (color_picker_event(
320             &point_layer->color_picker,
321             event,
322             &selected) < 0) {
323         return -1;
324     }
325
326     if (selected) {
327         return 0;
328     }
329
330     switch(event->type) {
331     case SDL_MOUSEBUTTONDOWN:
332     case SDL_MOUSEBUTTONUP:
333         return point_layer_mouse_button(
334             point_layer,
335             &event->button,
336             camera);
337
338     case SDL_KEYDOWN:
339     case SDL_KEYUP:
340         return point_layer_keyboard(
341             point_layer,
342             &event->key);
343
344     case SDL_TEXTINPUT:
345         return point_layer_text_input(
346             point_layer,
347             &event->text);
348     }
349
350     return 0;
351 }
352
353 size_t point_layer_count(const PointLayer *point_layer)
354 {
355     trace_assert(point_layer);
356     return dynarray_count(point_layer->points);
357 }
358
359 const Point *point_layer_points(const PointLayer *point_layer)
360 {
361     trace_assert(point_layer);
362     return dynarray_data(point_layer->points);
363 }
364
365 const Color *point_layer_colors(const PointLayer *point_layer)
366 {
367     trace_assert(point_layer);
368     return dynarray_data(point_layer->colors);
369 }
370
371 const char *point_layer_ids(const PointLayer *point_layer)
372 {
373     trace_assert(point_layer);
374     return dynarray_data(point_layer->ids);
375 }
376
377 int point_layer_dump_stream(const PointLayer *point_layer,
378                             FILE *filedump)
379 {
380     trace_assert(point_layer);
381     trace_assert(filedump);
382
383     size_t n = dynarray_count(point_layer->ids);
384     char *ids = dynarray_data(point_layer->ids);
385     Point *points = dynarray_data(point_layer->points);
386     Color *colors = dynarray_data(point_layer->colors);
387
388     fprintf(filedump, "%zd\n", n);
389     for (size_t i = 0; i < n; ++i) {
390         fprintf(filedump, "%s %f %f ",
391                 ids + ID_MAX_SIZE * i,
392                 points[i].x, points[i].y);
393         color_hex_to_stream(colors[i], filedump);
394         fprintf(filedump, "\n");
395     }
396
397     return 0;
398 }