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