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