]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
f59f4e18e147850fe7b4d194e2e97eb2bcca7c83
[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 typedef enum {
23     POINT_LAYER_IDLE = 0,
24     POINT_LAYER_EDIT_ID,
25     POINT_LAYER_MOVE
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_EDIT_ID &&
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         if (edit_field_render_world(
189                 point_layer->edit_field,
190                 camera,
191                 positions[point_layer->selected]) < 0) {
192             return -1;
193         }
194     }
195
196     if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
197         return -1;
198     }
199
200
201     return 0;
202 }
203
204 static
205 int point_layer_element_at(const PointLayer *point_layer,
206                            Point position)
207 {
208     trace_assert(point_layer);
209
210     int n = (int) dynarray_count(point_layer->positions);
211     Point *positions = dynarray_data(point_layer->positions);
212
213     for (int i = 0; i < n; ++i) {
214         if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
215             return i;
216         }
217     }
218
219     return -1;
220 }
221
222 static
223 int point_layer_add_element(PointLayer *point_layer,
224                             Point position,
225                             Color color)
226 {
227     trace_assert(point_layer);
228
229     char id[ID_MAX_SIZE];
230     for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
231         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
232     }
233     id[ID_MAX_SIZE - 1] = '\0';
234
235     dynarray_push(point_layer->positions, &position);
236     dynarray_push(point_layer->colors, &color);
237     dynarray_push(point_layer->ids, id);
238
239     return 0;
240 }
241
242 static
243 void point_layer_delete_nth_element(PointLayer *point_layer,
244                                     size_t i)
245 {
246     trace_assert(point_layer);
247     dynarray_delete_at(point_layer->positions, i);
248     dynarray_delete_at(point_layer->colors, i);
249     dynarray_delete_at(point_layer->ids, i);
250 }
251
252 static
253 int point_layer_idle_event(PointLayer *point_layer,
254                            const SDL_Event *event,
255                            const Camera *camera)
256 {
257     trace_assert(point_layer);
258     trace_assert(event);
259     trace_assert(camera);
260
261     int selected = 0;
262     if (color_picker_event(
263             &point_layer->color_picker,
264             event,
265             &selected) < 0) {
266         return -1;
267     }
268
269     if (selected) {
270         if (point_layer->selected >= 0) {
271             Color *colors = dynarray_data(point_layer->colors);
272             colors[point_layer->selected] =
273                 color_picker_rgba(&point_layer->color_picker);
274         }
275
276         return 0;
277     }
278
279     switch (event->type) {
280     case SDL_MOUSEBUTTONDOWN: {
281         switch (event->button.button) {
282         case SDL_BUTTON_LEFT: {
283             const Point position = camera_map_screen(camera, event->button.x, event->button.y);
284
285             point_layer->selected = point_layer_element_at(
286                 point_layer, position);
287
288             if (point_layer->selected < 0) {
289                 point_layer_add_element(
290                     point_layer, position, color_picker_rgba(&point_layer->color_picker));
291             } else {
292                 Color *colors = dynarray_data(point_layer->colors);
293                 point_layer->state = POINT_LAYER_MOVE;
294                 point_layer->color_picker =
295                     create_color_picker_from_rgba(colors[point_layer->selected]);
296             }
297         } break;
298         }
299     } break;
300
301     case SDL_KEYDOWN: {
302         switch (event->key.keysym.sym) {
303         case SDLK_DELETE: {
304             if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
305                 point_layer_delete_nth_element(point_layer, (size_t)point_layer->selected);
306                 point_layer->selected = -1;
307             }
308         } break;
309
310         case SDLK_F2: {
311             if (point_layer->selected >= 0) {
312                 char *ids = dynarray_data(point_layer->ids);
313                 point_layer->state = POINT_LAYER_EDIT_ID;
314                 edit_field_replace(
315                     point_layer->edit_field,
316                     ids + ID_MAX_SIZE * point_layer->selected);
317                 SDL_StartTextInput();
318             }
319         } break;
320         }
321     } break;
322     }
323
324     return 0;
325 }
326
327 static
328 int point_layer_edit_id_event(PointLayer *point_layer,
329                               const SDL_Event *event,
330                               const Camera *camera)
331 {
332     trace_assert(point_layer);
333     trace_assert(event);
334     trace_assert(camera);
335
336     switch (event->type) {
337     case SDL_KEYDOWN: {
338         switch(event->key.keysym.sym) {
339         case SDLK_RETURN: {
340             char *ids = dynarray_data(point_layer->ids);
341             const char *text = edit_field_as_text(point_layer->edit_field);
342             size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
343             memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
344             *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
345             point_layer->state = POINT_LAYER_IDLE;
346             SDL_StopTextInput();
347             return 0;
348         } break;
349
350         case SDLK_ESCAPE: {
351             point_layer->state = POINT_LAYER_IDLE;
352             SDL_StopTextInput();
353             return 0;
354         } break;
355         }
356     } break;
357     }
358
359     return edit_field_event(point_layer->edit_field, event);
360 }
361
362 static
363 int point_layer_move_event(PointLayer *point_layer,
364                            const SDL_Event *event,
365                            const Camera *camera)
366 {
367     trace_assert(point_layer);
368     trace_assert(event);
369     trace_assert(camera);
370     trace_assert(point_layer->selected >= 0);
371
372     switch (event->type) {
373     case SDL_MOUSEBUTTONUP: {
374         switch (event->button.button) {
375         case SDL_BUTTON_LEFT: {
376             point_layer->state = POINT_LAYER_IDLE;
377         } break;
378         }
379     } break;
380
381     case SDL_MOUSEMOTION: {
382         Point *positions = dynarray_data(point_layer->positions);
383         positions[point_layer->selected] =
384             camera_map_screen(camera, event->motion.x, event->motion.y);
385     } break;
386     }
387
388     return 0;
389 }
390
391 int point_layer_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
399     switch (point_layer->state) {
400     case POINT_LAYER_IDLE:
401         return point_layer_idle_event(point_layer, event, camera);
402
403     case POINT_LAYER_EDIT_ID:
404         return point_layer_edit_id_event(point_layer, event, camera);
405
406     case POINT_LAYER_MOVE:
407         return point_layer_move_event(point_layer, event, camera);
408     }
409
410     return 0;
411 }
412
413 size_t point_layer_count(const PointLayer *point_layer)
414 {
415     trace_assert(point_layer);
416     return dynarray_count(point_layer->positions);
417 }
418
419 const Point *point_layer_positions(const PointLayer *point_layer)
420 {
421     trace_assert(point_layer);
422     return dynarray_data(point_layer->positions);
423 }
424
425 const Color *point_layer_colors(const PointLayer *point_layer)
426 {
427     trace_assert(point_layer);
428     return dynarray_data(point_layer->colors);
429 }
430
431 const char *point_layer_ids(const PointLayer *point_layer)
432 {
433     trace_assert(point_layer);
434     return dynarray_data(point_layer->ids);
435 }
436
437 int point_layer_dump_stream(const PointLayer *point_layer,
438                             FILE *filedump)
439 {
440     trace_assert(point_layer);
441     trace_assert(filedump);
442
443     size_t n = dynarray_count(point_layer->ids);
444     char *ids = dynarray_data(point_layer->ids);
445     Point *positions = dynarray_data(point_layer->positions);
446     Color *colors = dynarray_data(point_layer->colors);
447
448     fprintf(filedump, "%zd\n", n);
449     for (size_t i = 0; i < n; ++i) {
450         fprintf(filedump, "%s %f %f ",
451                 ids + ID_MAX_SIZE * i,
452                 positions[i].x, positions[i].y);
453         color_hex_to_stream(colors[i], filedump);
454         fprintf(filedump, "\n");
455     }
456
457     return 0;
458 }