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