]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
(#788) Don't render Color Pickers of inactive Layers
[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     bool selected = false;
201     if (color_picker_mouse_button(
202             &point_layer->color_picker,
203             event,
204             &selected) < 0) {
205         return -1;
206     }
207
208     if (!selected &&
209         point_layer->state == POINT_LAYER_NORMAL_STATE &&
210         event->type == SDL_MOUSEBUTTONDOWN &&
211         event->button == SDL_BUTTON_LEFT) {
212         const int n = (int) dynarray_count(point_layer->points);
213         const Point *points = dynarray_data(point_layer->points);
214         const Point point = camera_map_screen(camera, event->x, event->y);
215
216         for (int i = 0; i < n; ++i) {
217             if (vec_length(vec_sub(points[i], point)) < POINT_LAYER_ELEMENT_RADIUS) {
218                 point_layer->selected = i;
219                 return 0;
220             }
221         }
222
223         char id[ID_MAX_SIZE];
224
225         for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
226             id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
227         }
228         id[ID_MAX_SIZE - 1] = '\0';
229
230         dynarray_push(point_layer->points, &point);
231         dynarray_push(point_layer->colors, &point_layer->color_picker.color);
232         dynarray_push(point_layer->ids, id);
233     }
234
235     return 0;
236 }
237
238 static
239 int point_layer_keyboard(PointLayer *point_layer,
240                          const SDL_KeyboardEvent *key)
241 {
242     trace_assert(point_layer);
243     trace_assert(key);
244
245     switch(point_layer->state) {
246     case POINT_LAYER_NORMAL_STATE: {
247         if (key->type == SDL_KEYDOWN) {
248             switch (key->keysym.sym) {
249             case SDLK_DELETE: {
250                 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->points)) {
251                     dynarray_delete_at(point_layer->points, (size_t) point_layer->selected);
252                     dynarray_delete_at(point_layer->colors, (size_t) point_layer->selected);
253                     dynarray_delete_at(point_layer->ids, (size_t) point_layer->selected);
254                 }
255                 point_layer->selected = -1;
256             } break;
257
258             case SDLK_F2: {
259                 if (point_layer->selected >= 0) {
260                     char *ids = dynarray_data(point_layer->ids);
261                     point_layer->state = POINT_LAYER_ID_EDITING_STATE;
262                     edit_field_replace(
263                         point_layer->edit_field,
264                         ids + ID_MAX_SIZE * point_layer->selected);
265                     SDL_StartTextInput();
266                 }
267             } break;
268
269             default: {}
270             }
271         }
272     } break;
273
274     case POINT_LAYER_ID_EDITING_STATE: {
275         if (edit_field_keyboard(point_layer->edit_field, key) < 0) {
276             return -1;
277         }
278
279         if (key->type == SDL_KEYDOWN) {
280             switch(key->keysym.sym) {
281             case SDLK_RETURN: {
282                 char *ids = dynarray_data(point_layer->ids);
283                 const char *text = edit_field_as_text(point_layer->edit_field);
284                 size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
285                 memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
286                 *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
287                 point_layer->state = POINT_LAYER_NORMAL_STATE;
288                 SDL_StopTextInput();
289             } break;
290
291             case SDLK_ESCAPE: {
292                 point_layer->state = POINT_LAYER_NORMAL_STATE;
293                 SDL_StopTextInput();
294             } break;
295             }
296         }
297     } break;
298     }
299
300
301     return 0;
302 }
303
304 static
305 int point_layer_text_input(PointLayer *point_layer,
306                            const SDL_TextInputEvent *text_input)
307 {
308     trace_assert(point_layer);
309     trace_assert(text_input);
310
311     if (point_layer->state == POINT_LAYER_ID_EDITING_STATE) {
312         /* TODO(#856): Special development keybindings interfere with id editing field */
313         return edit_field_text_input(point_layer->edit_field, text_input);
314     }
315
316     return 0;
317 }
318
319 int point_layer_event(PointLayer *point_layer,
320                       const SDL_Event *event,
321                       const Camera *camera)
322 {
323     trace_assert(point_layer);
324     trace_assert(event);
325     trace_assert(camera);
326
327     switch(event->type) {
328     case SDL_MOUSEBUTTONDOWN:
329     case SDL_MOUSEBUTTONUP:
330         return point_layer_mouse_button(
331             point_layer,
332             &event->button,
333             camera);
334
335     case SDL_KEYDOWN:
336     case SDL_KEYUP:
337         return point_layer_keyboard(
338             point_layer,
339             &event->key);
340
341     case SDL_TEXTINPUT:
342         return point_layer_text_input(
343             point_layer,
344             &event->text);
345     }
346
347     return 0;
348 }
349
350 size_t point_layer_count(const PointLayer *point_layer)
351 {
352     trace_assert(point_layer);
353     return dynarray_count(point_layer->points);
354 }
355
356 const Point *point_layer_points(const PointLayer *point_layer)
357 {
358     trace_assert(point_layer);
359     return dynarray_data(point_layer->points);
360 }
361
362 const Color *point_layer_colors(const PointLayer *point_layer)
363 {
364     trace_assert(point_layer);
365     return dynarray_data(point_layer->colors);
366 }
367
368 const char *point_layer_ids(const PointLayer *point_layer)
369 {
370     trace_assert(point_layer);
371     return dynarray_data(point_layer->ids);
372 }
373
374 int point_layer_dump_stream(const PointLayer *point_layer,
375                             FILE *filedump)
376 {
377     trace_assert(point_layer);
378     trace_assert(filedump);
379
380     size_t n = dynarray_count(point_layer->ids);
381     char *ids = dynarray_data(point_layer->ids);
382     Point *points = dynarray_data(point_layer->points);
383     Color *colors = dynarray_data(point_layer->colors);
384
385     fprintf(filedump, "%zd\n", n);
386     for (size_t i = 0; i < n; ++i) {
387         fprintf(filedump, "%s %f %f ",
388                 ids + ID_MAX_SIZE * i,
389                 points[i].x, points[i].y);
390         color_hex_to_stream(colors[i], filedump);
391         fprintf(filedump, "\n");
392     }
393
394     return 0;
395 }