]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
(#990) Implement changing color 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     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         if (point_layer->selected >= 0) {
273             Color *colors = dynarray_data(point_layer->colors);
274             colors[point_layer->selected] =
275                 color_picker_rgba(&point_layer->color_picker);
276         }
277
278         return 0;
279     }
280
281     switch (event->type) {
282     case SDL_MOUSEBUTTONDOWN: {
283         switch (event->button.button) {
284         case SDL_BUTTON_LEFT: {
285             const Point position = camera_map_screen(camera, event->button.x, event->button.y);
286
287             point_layer->selected = point_layer_element_at(
288                 point_layer, position);
289
290             if (point_layer->selected < 0) {
291                 point_layer_add_element(
292                     point_layer, position, color_picker_rgba(&point_layer->color_picker));
293             } else {
294                 Color *colors = dynarray_data(point_layer->colors);
295                 point_layer->state = POINT_LAYER_MOVE;
296                 point_layer->color_picker =
297                     create_color_picker_from_rgba(colors[point_layer->selected]);
298             }
299         } break;
300         }
301     } break;
302
303     case SDL_KEYDOWN: {
304         switch (event->key.keysym.sym) {
305         case SDLK_DELETE: {
306             if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
307                 point_layer_delete_nth_element(point_layer, (size_t)point_layer->selected);
308                 point_layer->selected = -1;
309             }
310         } break;
311
312         case SDLK_F2: {
313             if (point_layer->selected >= 0) {
314                 char *ids = dynarray_data(point_layer->ids);
315                 point_layer->state = POINT_LAYER_EDIT_ID;
316                 edit_field_replace(
317                     point_layer->edit_field,
318                     ids + ID_MAX_SIZE * point_layer->selected);
319                 SDL_StartTextInput();
320             }
321         } break;
322         }
323     } break;
324     }
325
326     return 0;
327 }
328
329 static
330 int point_layer_edit_id_event(PointLayer *point_layer,
331                               const SDL_Event *event,
332                               const Camera *camera)
333 {
334     trace_assert(point_layer);
335     trace_assert(event);
336     trace_assert(camera);
337
338     switch (event->type) {
339     case SDL_KEYDOWN: {
340         switch(event->key.keysym.sym) {
341         case SDLK_RETURN: {
342             char *ids = dynarray_data(point_layer->ids);
343             const char *text = edit_field_as_text(point_layer->edit_field);
344             size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
345             memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
346             *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
347             point_layer->state = POINT_LAYER_IDLE;
348             SDL_StopTextInput();
349             return 0;
350         } break;
351
352         case SDLK_ESCAPE: {
353             point_layer->state = POINT_LAYER_IDLE;
354             SDL_StopTextInput();
355             return 0;
356         } break;
357         }
358     } break;
359     }
360
361     return edit_field_event(point_layer->edit_field, event);
362 }
363
364 static
365 int point_layer_move_event(PointLayer *point_layer,
366                            const SDL_Event *event,
367                            const Camera *camera)
368 {
369     trace_assert(point_layer);
370     trace_assert(event);
371     trace_assert(camera);
372     trace_assert(point_layer->selected >= 0);
373
374     switch (event->type) {
375     case SDL_MOUSEBUTTONUP: {
376         switch (event->button.button) {
377         case SDL_BUTTON_LEFT: {
378             point_layer->state = POINT_LAYER_IDLE;
379         } break;
380         }
381     } break;
382
383     case SDL_MOUSEMOTION: {
384         Point *positions = dynarray_data(point_layer->positions);
385         positions[point_layer->selected] =
386             camera_map_screen(camera, event->motion.x, event->motion.y);
387     } break;
388     }
389
390     return 0;
391 }
392
393 int point_layer_event(PointLayer *point_layer,
394                       const SDL_Event *event,
395                       const Camera *camera)
396 {
397     trace_assert(point_layer);
398     trace_assert(event);
399     trace_assert(camera);
400
401     switch (point_layer->state) {
402     case POINT_LAYER_IDLE:
403         return point_layer_idle_event(point_layer, event, camera);
404
405     case POINT_LAYER_EDIT_ID:
406         return point_layer_edit_id_event(point_layer, event, camera);
407
408     case POINT_LAYER_MOVE:
409         return point_layer_move_event(point_layer, event, camera);
410     }
411
412     return 0;
413 }
414
415 size_t point_layer_count(const PointLayer *point_layer)
416 {
417     trace_assert(point_layer);
418     return dynarray_count(point_layer->positions);
419 }
420
421 const Point *point_layer_positions(const PointLayer *point_layer)
422 {
423     trace_assert(point_layer);
424     return dynarray_data(point_layer->positions);
425 }
426
427 const Color *point_layer_colors(const PointLayer *point_layer)
428 {
429     trace_assert(point_layer);
430     return dynarray_data(point_layer->colors);
431 }
432
433 const char *point_layer_ids(const PointLayer *point_layer)
434 {
435     trace_assert(point_layer);
436     return dynarray_data(point_layer->ids);
437 }
438
439 int point_layer_dump_stream(const PointLayer *point_layer,
440                             FILE *filedump)
441 {
442     trace_assert(point_layer);
443     trace_assert(filedump);
444
445     size_t n = dynarray_count(point_layer->ids);
446     char *ids = dynarray_data(point_layer->ids);
447     Point *positions = dynarray_data(point_layer->positions);
448     Color *colors = dynarray_data(point_layer->colors);
449
450     fprintf(filedump, "%zd\n", n);
451     for (size_t i = 0; i < n; ++i) {
452         fprintf(filedump, "%s %f %f ",
453                 ids + ID_MAX_SIZE * i,
454                 positions[i].x, positions[i].y);
455         color_hex_to_stream(colors[i], filedump);
456         fprintf(filedump, "\n");
457     }
458
459     return 0;
460 }