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