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