]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
Merge pull request #1011 from tsoding/1002
[nothing.git] / src / game / level / level_editor / rect_layer.c
1 #include "game/camera.h"
2 #include "system/lt.h"
3 #include "system/stacktrace.h"
4 #include "system/nth_alloc.h"
5 #include "system/log.h"
6 #include "math/rect.h"
7 #include "color.h"
8 #include "rect_layer.h"
9 #include "dynarray.h"
10 #include "system/line_stream.h"
11 #include "color_picker.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14
15 #define RECT_LAYER_ID_MAX_SIZE 36
16 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
17 #define CREATE_AREA_THRESHOLD 10.0
18
19 // TODO(#1003): RectLayer does not support UndoHistory
20
21 typedef enum {
22     RECT_LAYER_IDLE = 0,
23     RECT_LAYER_CREATE,
24     // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
25     RECT_LAYER_RESIZE,
26     RECT_LAYER_MOVE,
27     RECT_LAYER_ID_RENAME,
28 } RectLayerState;
29
30 struct RectLayer {
31     Lt *lt;
32     RectLayerState state;
33     Dynarray *ids;
34     Dynarray *rects;
35     Dynarray *colors;
36     ColorPicker color_picker;
37     Vec create_begin;
38     Vec create_end;
39     int selection;
40     Vec move_anchor;
41     Edit_field *id_edit_field;
42 };
43
44 typedef int (*EventHandler)(RectLayer *layer, const SDL_Event *event, const Camera *camera);
45
46 static int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color)
47 {
48     trace_assert(layer);
49
50     if (dynarray_push(layer->rects, &rect) < 0) {
51         return -1;
52     }
53
54     if (dynarray_push(layer->colors, &color) < 0) {
55         return -1;
56     }
57
58     char id[RECT_LAYER_ID_MAX_SIZE];
59     for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
60         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
61     }
62     id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
63
64     if (dynarray_push(layer->ids, id)) {
65         return -1;
66     }
67
68     return 0;
69 }
70
71 // TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
72 static int rect_layer_rect_at(RectLayer *layer, Vec position)
73 {
74     trace_assert(layer);
75
76     const size_t n = dynarray_count(layer->rects);
77     Rect *rects = dynarray_data(layer->rects);
78
79     for (size_t i = 0; i < n; ++i) {
80         if (rect_contains_point(rects[i], position)) {
81             return (int) i;
82         }
83     }
84
85     return -1;
86 }
87
88 static Rect rect_layer_resize_anchor(const RectLayer *layer, const Camera *camera, size_t i)
89 {
90     Rect *rects = dynarray_data(layer->rects);
91     const Rect overlay_rect =
92         rect_scale(
93             camera_rect(camera, rects[i]),
94             RECT_LAYER_SELECTION_THICCNESS * 0.5f);
95
96     return rect(
97         overlay_rect.x + overlay_rect.w,
98         overlay_rect.y + overlay_rect.h,
99         RECT_LAYER_SELECTION_THICCNESS * 2.0f,
100         RECT_LAYER_SELECTION_THICCNESS * 2.0f);
101 }
102
103 static int rect_layer_delete_rect_at(RectLayer *layer, size_t i)
104 {
105     trace_assert(layer);
106
107     dynarray_delete_at(layer->rects, i);
108     dynarray_delete_at(layer->colors, i);
109     dynarray_delete_at(layer->ids, i);
110
111     return 0;
112 }
113
114 static int rect_layer_event_idle(RectLayer *layer, const SDL_Event *event, const Camera *camera)
115 {
116     trace_assert(layer);
117     trace_assert(event);
118     trace_assert(camera);
119
120     switch (event->type) {
121     case SDL_MOUSEBUTTONDOWN: {
122         switch (event->button.button) {
123         case SDL_BUTTON_LEFT: {
124             Point position = camera_map_screen(
125                 camera,
126                 event->button.x,
127                 event->button.y);
128             int rect_at_position =
129                 rect_layer_rect_at(layer, position);
130
131             if (rect_at_position >= 0) {
132                 Rect *rects = dynarray_data(layer->rects);
133                 Color *colors = dynarray_data(layer->colors);
134                 layer->selection = rect_at_position;
135                 layer->state = RECT_LAYER_MOVE;
136                 layer->move_anchor =
137                     vec_sub(
138                         position,
139                         vec(
140                             rects[layer->selection].x,
141                             rects[layer->selection].y));
142                 layer->color_picker =
143                     create_color_picker_from_rgba(colors[rect_at_position]);
144             } else if (layer->selection >= 0 && rect_contains_point(
145                            rect_layer_resize_anchor(
146                                layer,
147                                camera,
148                                (size_t)layer->selection),
149                            vec(
150                                (float) event->button.x,
151                                (float) event->button.y))) {
152                 layer->state = RECT_LAYER_RESIZE;
153             } else {
154                 layer->selection = rect_at_position;
155
156                 if (layer->selection < 0) {
157                     layer->state = RECT_LAYER_CREATE;
158                     layer->create_begin = position;
159                     layer->create_end = position;
160                 }
161             }
162         } break;
163         }
164     } break;
165
166     case SDL_KEYDOWN: {
167         switch (event->key.keysym.sym) {
168         case SDLK_DELETE: {
169             if (layer->selection >= 0) {
170                 rect_layer_delete_rect_at(layer, (size_t) layer->selection);
171                 layer->selection = -1;
172             }
173         } break;
174
175         case SDLK_F2: {
176             if (layer->selection >= 0) {
177                 const char *ids = dynarray_data(layer->ids);
178                 layer->state = RECT_LAYER_ID_RENAME;
179                 edit_field_replace(
180                     layer->id_edit_field,
181                     ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
182                 SDL_StartTextInput();
183             }
184         } break;
185         }
186     } break;
187     }
188
189     return 0;
190 }
191
192 static int rect_layer_event_create(RectLayer *layer, const SDL_Event *event, const Camera *camera)
193 {
194     trace_assert(layer);
195     trace_assert(event);
196     trace_assert(camera);
197
198     switch (event->type) {
199     case SDL_MOUSEBUTTONUP: {
200         switch (event->button.button) {
201         case SDL_BUTTON_LEFT: {
202             const Rect real_rect =
203                 rect_from_points(
204                     layer->create_begin,
205                     layer->create_end);
206             const float area = real_rect.w * real_rect.h;
207
208             if (area >= CREATE_AREA_THRESHOLD) {
209                 rect_layer_add_rect(
210                     layer,
211                     real_rect,
212                     color_picker_rgba(&layer->color_picker));
213             } else {
214                 log_info("The area is too small %f. Such small box won't be created.\n", area);
215             }
216             layer->state = RECT_LAYER_IDLE;
217         } break;
218         }
219     } break;
220
221     case SDL_MOUSEMOTION: {
222         layer->create_end = camera_map_screen(
223             camera,
224             event->motion.x,
225             event->motion.y);
226     } break;
227     }
228     return 0;
229 }
230
231 static int rect_layer_event_resize(RectLayer *layer, const SDL_Event *event, const Camera *camera)
232 {
233     trace_assert(layer);
234     trace_assert(event);
235     trace_assert(camera);
236
237     switch (event->type) {
238     case SDL_MOUSEMOTION: {
239         Rect *rects = dynarray_data(layer->rects);
240         trace_assert(layer->selection >= 0);
241         rects[layer->selection] = rect_from_points(
242             vec(rects[layer->selection].x, rects[layer->selection].y),
243             vec_sum(
244                 camera_map_screen(
245                     camera,
246                     event->button.x,
247                     event->button.y),
248                 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
249                     RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
250     } break;
251
252     case SDL_MOUSEBUTTONUP: {
253         layer->state = RECT_LAYER_IDLE;
254     } break;
255     }
256
257     return 0;
258 }
259
260 static int rect_layer_event_move(RectLayer *layer, const SDL_Event *event, const Camera *camera)
261 {
262     trace_assert(layer);
263     trace_assert(event);
264     trace_assert(camera);
265
266     switch (event->type) {
267     case SDL_MOUSEMOTION: {
268         Point position = vec_sub(
269             camera_map_screen(
270                 camera,
271                 event->button.x,
272                 event->button.y),
273             layer->move_anchor);
274
275         Rect *rects = dynarray_data(layer->rects);
276
277         trace_assert(layer->selection >= 0);
278
279         rects[layer->selection].x = position.x;
280         rects[layer->selection].y = position.y;
281     } break;
282
283     case SDL_MOUSEBUTTONUP: {
284         layer->state = RECT_LAYER_IDLE;
285     } break;
286     }
287     return 0;
288 }
289
290 static int rect_layer_event_id_rename(RectLayer *layer, const SDL_Event *event, const Camera *camera)
291 {
292     trace_assert(layer);
293     trace_assert(event);
294     trace_assert(camera);
295
296     switch (event->type) {
297     case SDL_KEYDOWN: {
298         switch (event->key.keysym.sym) {
299         case SDLK_RETURN: {
300             char *id =
301                 (char *)dynarray_data(layer->ids) + layer->selection * RECT_LAYER_ID_MAX_SIZE;
302             memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
303             memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
304             layer->state = RECT_LAYER_IDLE;
305             SDL_StopTextInput();
306         } break;
307
308         case SDLK_ESCAPE: {
309             layer->state = RECT_LAYER_IDLE;
310             SDL_StopTextInput();
311         } break;
312         }
313     } break;
314     }
315
316     return edit_field_event(layer->id_edit_field, event);
317 }
318
319 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
320 {
321     LayerPtr layer = {
322         .type = LAYER_RECT,
323         .ptr = rect_layer
324     };
325     return layer;
326 }
327
328 RectLayer *create_rect_layer(void)
329 {
330     Lt *lt = create_lt();
331
332     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
333     if (layer == NULL) {
334         RETURN_LT(lt, NULL);
335     }
336     layer->lt = lt;
337
338     layer->ids = PUSH_LT(
339         lt,
340         create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
341         destroy_dynarray);
342     if (layer->ids == NULL) {
343         RETURN_LT(lt, NULL);
344     }
345
346     layer->rects = PUSH_LT(
347         lt,
348         create_dynarray(sizeof(Rect)),
349         destroy_dynarray);
350     if (layer->rects == NULL) {
351         RETURN_LT(lt, NULL);
352     }
353
354     layer->colors = PUSH_LT(
355         lt,
356         create_dynarray(sizeof(Color)),
357         destroy_dynarray);
358     if (layer->colors == NULL) {
359         RETURN_LT(lt, NULL);
360     }
361
362     layer->id_edit_field = PUSH_LT(
363         lt,
364         create_edit_field(
365             vec(3.0f, 3.0f),
366             COLOR_BLACK),
367         destroy_edit_field);
368     if (layer->id_edit_field == NULL) {
369         RETURN_LT(lt, NULL);
370     }
371
372     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
373     layer->selection = -1;
374
375     return layer;
376 }
377
378 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
379 {
380     trace_assert(line_stream);
381
382     RectLayer *layer = create_rect_layer();
383     if (layer == NULL) {
384         return NULL;
385     }
386
387     const char *line = line_stream_next(line_stream);
388     if (line == NULL) {
389         RETURN_LT(layer->lt, NULL);
390     }
391
392     size_t count = 0;
393     if (sscanf(line, "%zu", &count) < 0) {
394         RETURN_LT(layer->lt, NULL);
395     }
396
397     for (size_t i = 0; i < count; ++i) {
398         line = line_stream_next(line_stream);
399         if (line == NULL) {
400             RETURN_LT(layer->lt, NULL);
401         }
402
403         char hex[7];
404         Rect rect;
405         char id[RECT_LAYER_ID_MAX_SIZE];
406
407         if (sscanf(line,
408                    "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
409                    id,
410                    &rect.x, &rect.y,
411                    &rect.w, &rect.h,
412                    hex) < 0) {
413             RETURN_LT(layer->lt, NULL);
414         }
415
416         Color color = hexstr(hex);
417
418         dynarray_push(layer->rects, &rect);
419         dynarray_push(layer->ids, id);
420         dynarray_push(layer->colors, &color);
421     }
422
423     return layer;
424 }
425
426 void destroy_rect_layer(RectLayer *layer)
427 {
428     trace_assert(layer);
429     RETURN_LT0(layer->lt);
430 }
431
432 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
433 {
434     trace_assert(layer);
435     trace_assert(camera);
436
437     const size_t n = dynarray_count(layer->rects);
438     Rect *rects = dynarray_data(layer->rects);
439     Color *colors = dynarray_data(layer->colors);
440     const char *ids = dynarray_data(layer->ids);
441
442     // The Rectangles
443     for (size_t i = 0; i < n; ++i) {
444         if (camera_fill_rect(
445                 camera,
446                 rects[i],
447                 color_scale(
448                     colors[i],
449                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
450             return -1;
451         }
452     }
453
454     // Proto Rectangle
455     const Color color = color_picker_rgba(&layer->color_picker);
456     if (layer->state == RECT_LAYER_CREATE) {
457         if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
458             return -1;
459         }
460     }
461
462     // ID renaming Edit Field
463     if (layer->state == RECT_LAYER_ID_RENAME) {
464         if (edit_field_render_screen(layer->id_edit_field, camera, vec(400.0f, 400.0f)) < 0) {
465             return -1;
466         }
467     }
468
469     // Selection Overlay
470     if (active && layer->selection >= 0) {
471         const Rect overlay_rect =
472             rect_scale(
473                 camera_rect(camera, rects[layer->selection]),
474                 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
475         const Color overlay_color = color_invert(colors[layer->selection]);
476
477         // Main Rectangle
478         if (camera_fill_rect(
479                 camera,
480                 rects[layer->selection],
481                 color_scale(
482                     colors[layer->selection],
483                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
484             return -1;
485         }
486
487         if (camera_draw_thicc_rect_screen(
488                 camera,
489                 overlay_rect,
490                 overlay_color,
491                 RECT_LAYER_SELECTION_THICCNESS) < 0) {
492             return -1;
493         }
494
495         // Rectangle Id
496         if (camera_render_text(
497                 camera,
498                 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
499                 vec(3.0f, 3.0f),
500                 color_invert(colors[layer->selection]),
501                 rect_position(rects[layer->selection])) < 0) {
502             return -1;
503         }
504
505         // Resize Anchor
506         if (camera_fill_rect_screen(
507                 camera,
508                 rect_layer_resize_anchor(layer, camera, (size_t) layer->selection),
509                 overlay_color) < 0) {
510             return -1;
511         }
512     }
513
514     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
515         return -1;
516     }
517
518     return 0;
519 }
520
521 int rect_layer_event(RectLayer *layer,
522                      const SDL_Event *event,
523                      const Camera *camera,
524                      UndoHistory *undo_history)
525 {
526     trace_assert(layer);
527     trace_assert(event);
528     trace_assert(undo_history);
529
530     int selected = 0;
531     if (color_picker_event(&layer->color_picker, event, &selected) < 0) {
532         return -1;
533     }
534
535     if (selected) {
536         if (layer->selection >= 0) {
537             Color *colors = dynarray_data(layer->colors);
538             colors[layer->selection] = color_picker_rgba(&layer->color_picker);
539         }
540
541         return 0;
542     }
543
544     switch (layer->state) {
545     case RECT_LAYER_IDLE:
546         return rect_layer_event_idle(layer, event, camera);
547
548     case RECT_LAYER_CREATE:
549         return rect_layer_event_create(layer, event, camera);
550
551     case RECT_LAYER_RESIZE:
552         return rect_layer_event_resize(layer, event, camera);
553
554     case RECT_LAYER_MOVE:
555         return rect_layer_event_move(layer, event, camera);
556
557     case RECT_LAYER_ID_RENAME:
558         return rect_layer_event_id_rename(layer, event, camera);
559     }
560
561     return 0;
562 }
563
564 size_t rect_layer_count(const RectLayer *layer)
565 {
566     return dynarray_count(layer->rects);
567 }
568
569 const Rect *rect_layer_rects(const RectLayer *layer)
570 {
571     return dynarray_data(layer->rects);
572 }
573
574 const Color *rect_layer_colors(const RectLayer *layer)
575 {
576     return dynarray_data(layer->colors);
577 }
578
579 const char *rect_layer_ids(const RectLayer *layer)
580 {
581     return dynarray_data(layer->ids);
582 }
583
584 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
585 {
586     trace_assert(layer);
587     trace_assert(filedump);
588
589     size_t n = dynarray_count(layer->ids);
590     char *ids = dynarray_data(layer->ids);
591     Rect *rects = dynarray_data(layer->rects);
592     Color *colors = dynarray_data(layer->colors);
593
594     fprintf(filedump, "%zd\n", n);
595     for (size_t i = 0; i < n; ++i) {
596         fprintf(filedump, "%s %f %f %f %f ",
597                 ids + RECT_LAYER_ID_MAX_SIZE * i,
598                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
599         color_hex_to_stream(colors[i], filedump);
600         fprintf(filedump, "\n");
601     }
602
603     return 0;
604 }