3 #include "game/camera.h"
5 #include "system/stacktrace.h"
6 #include "system/nth_alloc.h"
7 #include "system/log.h"
10 #include "rect_layer.h"
12 #include "system/line_stream.h"
13 #include "color_picker.h"
14 #include "system/str.h"
15 #include "ui/edit_field.h"
16 #include "undo_history.h"
17 #include "game/level/action.h"
19 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
20 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
21 #define CREATE_AREA_THRESHOLD 10.0
23 static int clipboard = 0;
24 static Rect clipboard_rect;
25 static Color clipboard_color;
30 // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
44 ColorPicker color_picker;
49 Edit_field *id_edit_field;
53 const char *id_name_prefix;
71 char id[ENTITY_MAX_ID_SIZE];
92 UndoElementContext element;
97 UndoContext create_undo_add_context(RectLayer *layer, size_t index)
100 trace_assert(index < dynarray_count(layer->rects));
102 UndoContext undo_context;
103 undo_context.add.type = UNDO_ADD;
104 undo_context.add.layer = layer;
105 undo_context.add.index = index;
110 UndoContext create_undo_element_context(RectLayer *layer)
113 size_t index = (size_t) layer->selection;
114 trace_assert(index < dynarray_count(layer->rects));
116 UndoContext undo_context;
117 undo_context.element.layer = layer;
118 undo_context.element.index = index;
119 dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
120 dynarray_copy_to(layer->colors, &undo_context.element.color, index);
121 dynarray_copy_to(layer->ids, undo_context.element.id, index);
122 dynarray_copy_to(layer->actions, &undo_context.element.action, index);
127 UndoContext create_undo_update_context(RectLayer *rect_layer)
129 UndoContext undo_context = create_undo_element_context(rect_layer);
130 undo_context.type = UNDO_UPDATE;
135 UndoContext create_undo_delete_context(RectLayer *rect_layer)
137 UndoContext undo_context = create_undo_element_context(rect_layer);
138 undo_context.type = UNDO_DELETE;
143 UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
145 UndoContext undo_context;
146 undo_context.swap.type = UNDO_SWAP;
147 undo_context.swap.layer = rect_layer;
148 undo_context.swap.index1 = index1;
149 undo_context.swap.index2 = index2;
154 void rect_layer_undo(void *context, size_t context_size)
156 trace_assert(context);
157 trace_assert(sizeof(UndoContext) == context_size);
159 UndoContext *undo_context = context;
161 switch (undo_context->type) {
163 RectLayer *layer = undo_context->add.layer;
164 dynarray_delete_at(layer->rects, undo_context->add.index);
165 dynarray_delete_at(layer->colors, undo_context->add.index);
166 dynarray_delete_at(layer->ids, undo_context->add.index);
167 dynarray_delete_at(layer->actions, undo_context->add.index);
168 layer->selection = -1;
172 RectLayer *layer = undo_context->element.layer;
173 dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
174 dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
175 dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
176 dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
177 layer->selection = -1;
181 RectLayer *layer = undo_context->element.layer;
182 dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
183 dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
184 dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
185 dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
189 RectLayer *layer = undo_context->element.layer;
190 dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
191 dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
192 dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
193 dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
198 #define UNDO_PUSH(HISTORY, CONTEXT) \
200 UndoContext context = (CONTEXT); \
208 static int rect_layer_add_rect(RectLayer *layer,
211 UndoHistory *undo_history)
215 if (dynarray_push(layer->rects, &rect) < 0) {
219 if (dynarray_push(layer->colors, &color) < 0) {
223 char id[ENTITY_MAX_ID_SIZE];
224 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
225 layer->id_name_prefix,
226 layer->id_name_counter++);
227 if (dynarray_push(layer->ids, id)) {
231 dynarray_push_empty(layer->actions);
235 create_undo_add_context(
237 dynarray_count(layer->rects) - 1));
242 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
246 int n = (int) dynarray_count(layer->rects);
247 Rect *rects = dynarray_data(layer->rects);
249 for (int i = n - 1; i >= 0; --i) {
250 if (rect_contains_point(rects[i], position)) {
258 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
259 UndoHistory *undo_history)
262 trace_assert(a < dynarray_count(layer->rects));
263 trace_assert(b < dynarray_count(layer->rects));
265 dynarray_swap(layer->rects, a, b);
266 dynarray_swap(layer->colors, a, b);
267 dynarray_swap(layer->ids, a, b);
268 dynarray_swap(layer->actions, a, b);
270 UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b));
273 static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
275 const Rect overlay_rect =
277 camera_rect(camera, boundary_rect),
278 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
281 overlay_rect.x + overlay_rect.w,
282 overlay_rect.y + overlay_rect.h,
283 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
284 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
287 static int rect_layer_delete_rect_at(RectLayer *layer,
289 UndoHistory *undo_history)
293 UNDO_PUSH(undo_history, create_undo_delete_context(layer));
295 dynarray_delete_at(layer->rects, i);
296 dynarray_delete_at(layer->colors, i);
297 dynarray_delete_at(layer->ids, i);
298 dynarray_delete_at(layer->actions, i);
303 static int rect_layer_event_idle(RectLayer *layer,
304 const SDL_Event *event,
305 const Camera *camera,
306 UndoHistory *undo_history)
310 trace_assert(camera);
312 int color_changed = 0;
313 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
318 if (layer->selection >= 0) {
319 dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
320 layer->state = RECT_LAYER_RECOLOR;
325 switch (event->type) {
326 case SDL_MOUSEBUTTONDOWN: {
327 switch (event->button.button) {
328 case SDL_BUTTON_LEFT: {
329 Vec2f position = camera_map_screen(
333 int rect_at_position =
334 rect_layer_rect_at(layer, position);
336 Rect *rects = dynarray_data(layer->rects);
337 Color *colors = dynarray_data(layer->colors);
339 if (layer->selection >= 0 && rect_contains_point(
340 rect_layer_resize_anchor(
342 rects[layer->selection]),
344 (float) event->button.x,
345 (float) event->button.y))) {
346 layer->state = RECT_LAYER_RESIZE;
347 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
348 } else if (rect_at_position >= 0) {
349 layer->selection = rect_at_position;
350 layer->state = RECT_LAYER_MOVE;
355 rects[layer->selection].x,
356 rects[layer->selection].y));
357 layer->color_picker =
358 create_color_picker_from_rgba(colors[rect_at_position]);
360 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
362 layer->selection = rect_at_position;
364 if (layer->selection < 0) {
365 layer->state = RECT_LAYER_CREATE;
366 layer->create_begin = position;
367 layer->create_end = position;
375 switch (event->key.keysym.sym) {
377 if ((event->key.keysym.mod & KMOD_SHIFT)
378 && (layer->selection >= 0)
379 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
380 rect_layer_swap_elements(
382 (size_t) layer->selection,
383 (size_t) layer->selection + 1,
390 if ((event->key.keysym.mod & KMOD_SHIFT)
391 && (layer->selection > 0)
392 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
393 rect_layer_swap_elements(
395 (size_t) layer->selection,
396 (size_t) layer->selection - 1,
403 if (layer->selection >= 0) {
404 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
405 layer->selection = -1;
410 if (layer->selection >= 0) {
411 const char *ids = dynarray_data(layer->ids);
412 Color *colors = dynarray_data(layer->colors);
415 layer->id_edit_field,
416 RECT_LAYER_ID_LABEL_SIZE,
417 color_invert(colors[layer->selection]));
419 layer->state = RECT_LAYER_ID_RENAME;
421 layer->id_edit_field,
422 ids + layer->selection * ENTITY_MAX_ID_SIZE);
423 SDL_StartTextInput();
428 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
430 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
431 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
436 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
438 SDL_GetMouseState(&x, &y);
439 Vec2f position = camera_map_screen(camera, x, y);
443 rect(position.x, position.y,
444 clipboard_rect.w, clipboard_rect.h),
456 static int rect_layer_event_create(RectLayer *layer,
457 const SDL_Event *event,
458 const Camera *camera,
459 UndoHistory *undo_history)
463 trace_assert(camera);
465 switch (event->type) {
466 case SDL_MOUSEBUTTONUP: {
467 switch (event->button.button) {
468 case SDL_BUTTON_LEFT: {
469 const Rect real_rect =
473 const float area = real_rect.w * real_rect.h;
475 if (area >= CREATE_AREA_THRESHOLD) {
479 color_picker_rgba(&layer->color_picker),
482 log_info("The area is too small %f. Such small box won't be created.\n", area);
484 layer->state = RECT_LAYER_IDLE;
489 case SDL_MOUSEMOTION: {
490 layer->create_end = camera_map_screen(
499 static int rect_layer_event_resize(RectLayer *layer,
500 const SDL_Event *event,
501 const Camera *camera,
502 UndoHistory *undo_history)
506 trace_assert(camera);
507 trace_assert(layer->selection >= 0);
509 switch (event->type) {
510 case SDL_MOUSEMOTION: {
511 layer->inter_rect = rect_from_points(
512 vec(layer->inter_rect.x, layer->inter_rect.y),
518 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
519 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
522 case SDL_MOUSEBUTTONUP: {
523 layer->state = RECT_LAYER_IDLE;
524 UNDO_PUSH(undo_history, create_undo_update_context(layer));
525 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
532 static int rect_layer_event_move(RectLayer *layer,
533 const SDL_Event *event,
534 const Camera *camera,
535 UndoHistory *undo_history)
539 trace_assert(camera);
540 trace_assert(layer->selection >= 0);
542 Rect *rects = dynarray_data(layer->rects);
544 switch (event->type) {
545 case SDL_MOUSEMOTION: {
546 Vec2f position = vec_sub(
553 trace_assert(layer->selection >= 0);
555 layer->inter_rect.x = position.x;
556 layer->inter_rect.y = position.y;
559 case SDL_MOUSEBUTTONUP: {
560 layer->state = RECT_LAYER_IDLE;
562 float distance = vec_length(
563 vec_sub(rect_position(layer->inter_rect),
564 rect_position(rects[layer->selection])));
566 if (distance > 1e-6) {
567 UNDO_PUSH(undo_history, create_undo_update_context(layer));
568 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
575 static int rect_layer_event_id_rename(RectLayer *layer,
576 const SDL_Event *event,
577 const Camera *camera,
578 UndoHistory *undo_history)
582 trace_assert(camera);
583 trace_assert(layer->selection >= 0);
585 switch (event->type) {
587 switch (event->key.keysym.sym) {
589 UNDO_PUSH(undo_history, create_undo_update_context(layer));
591 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
592 memset(id, 0, ENTITY_MAX_ID_SIZE);
593 memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
594 layer->state = RECT_LAYER_IDLE;
599 layer->state = RECT_LAYER_IDLE;
606 return edit_field_event(layer->id_edit_field, event);
609 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
618 RectLayer *create_rect_layer(const char *id_name_prefix)
620 Lt *lt = create_lt();
622 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
628 layer->ids = PUSH_LT(
630 create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
632 if (layer->ids == NULL) {
636 layer->rects = PUSH_LT(
638 create_dynarray(sizeof(Rect)),
640 if (layer->rects == NULL) {
644 layer->colors = PUSH_LT(
646 create_dynarray(sizeof(Color)),
648 if (layer->colors == NULL) {
652 layer->actions = PUSH_LT(
654 create_dynarray(sizeof(Action)),
656 if (layer->actions == NULL) {
660 layer->id_edit_field = PUSH_LT(
663 RECT_LAYER_ID_LABEL_SIZE,
666 if (layer->id_edit_field == NULL) {
670 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
671 layer->selection = -1;
672 layer->id_name_prefix = id_name_prefix;
677 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
679 trace_assert(line_stream);
681 RectLayer *layer = create_rect_layer(id_name_prefix);
686 const char *line = line_stream_next(line_stream);
688 RETURN_LT(layer->lt, NULL);
692 if (sscanf(line, "%zu", &count) < 0) {
693 RETURN_LT(layer->lt, NULL);
696 for (size_t i = 0; i < count; ++i) {
697 line = line_stream_next(line_stream);
699 RETURN_LT(layer->lt, NULL);
704 char id[ENTITY_MAX_ID_SIZE];
708 "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
713 log_fail("%s\n", strerror(errno));
714 RETURN_LT(layer->lt, NULL);
718 Color color = hexstr(hex);
719 dynarray_push(layer->rects, &rect);
720 dynarray_push(layer->ids, id);
721 dynarray_push(layer->colors, &color);
725 if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
727 switch (action.type) {
728 case ACTION_NONE: break;
730 case ACTION_TOGGLE_GOAL:
731 case ACTION_HIDE_LABEL: {
732 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
733 log_fail("%s\n", strerror(errno));
734 RETURN_LT(layer->lt, NULL);
738 case ACTION_N: break;
742 dynarray_push(layer->actions, &action);
748 void destroy_rect_layer(RectLayer *layer)
751 RETURN_LT0(layer->lt);
754 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
757 trace_assert(camera);
759 const size_t n = dynarray_count(layer->rects);
760 Rect *rects = dynarray_data(layer->rects);
761 Color *colors = dynarray_data(layer->colors);
762 const char *ids = dynarray_data(layer->ids);
765 for (size_t i = 0; i < n; ++i) {
766 Rect rect = rects[i];
767 Color color = colors[i];
769 if (layer->selection == (int) i) {
770 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
771 rect = layer->inter_rect;
774 if (layer->state == RECT_LAYER_RECOLOR) {
775 color = layer->inter_color;
779 if (camera_fill_rect(
784 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
789 if (active && (size_t) layer->selection == i) {
790 const Rect overlay_rect =
792 camera_rect(camera, rect),
793 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
794 const Color overlay_color = color_invert(color);
797 if (camera_fill_rect(
802 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
806 if (camera_draw_thicc_rect_screen(
810 RECT_LAYER_SELECTION_THICCNESS) < 0) {
815 if (layer->state == RECT_LAYER_ID_RENAME) {
816 // ID renaming Edit Field
817 if (edit_field_render_world(
818 layer->id_edit_field,
820 rect_position(rect)) < 0) {
825 if (camera_render_text(
827 ids + layer->selection * ENTITY_MAX_ID_SIZE,
828 RECT_LAYER_ID_LABEL_SIZE,
830 rect_position(rect)) < 0) {
836 if (camera_fill_rect_screen(
838 rect_layer_resize_anchor(camera, rect),
839 overlay_color) < 0) {
846 const Color color = color_picker_rgba(&layer->color_picker);
847 if (layer->state == RECT_LAYER_CREATE) {
848 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
853 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
861 int rect_layer_event_recolor(RectLayer *layer,
862 const SDL_Event *event,
863 const Camera *camera,
864 UndoHistory *undo_history)
868 trace_assert(camera);
869 trace_assert(undo_history);
870 trace_assert(layer->selection >= 0);
872 int color_changed = 0;
873 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
878 layer->inter_color = color_picker_rgba(&layer->color_picker);
880 if (!color_picker_drag(&layer->color_picker)) {
881 UNDO_PUSH(undo_history, create_undo_update_context(layer));
882 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
883 layer->state = RECT_LAYER_IDLE;
890 int rect_layer_event(RectLayer *layer,
891 const SDL_Event *event,
892 const Camera *camera,
893 UndoHistory *undo_history)
897 trace_assert(undo_history);
899 switch (layer->state) {
900 case RECT_LAYER_IDLE:
901 return rect_layer_event_idle(layer, event, camera, undo_history);
903 case RECT_LAYER_CREATE:
904 return rect_layer_event_create(layer, event, camera, undo_history);
906 case RECT_LAYER_RESIZE:
907 return rect_layer_event_resize(layer, event, camera, undo_history);
909 case RECT_LAYER_MOVE:
910 return rect_layer_event_move(layer, event, camera, undo_history);
912 case RECT_LAYER_ID_RENAME:
913 return rect_layer_event_id_rename(layer, event, camera, undo_history);
915 case RECT_LAYER_RECOLOR:
916 return rect_layer_event_recolor(layer, event, camera, undo_history);
922 size_t rect_layer_count(const RectLayer *layer)
924 return dynarray_count(layer->rects);
927 const Rect *rect_layer_rects(const RectLayer *layer)
929 return dynarray_data(layer->rects);
932 const Color *rect_layer_colors(const RectLayer *layer)
934 return dynarray_data(layer->colors);
937 const char *rect_layer_ids(const RectLayer *layer)
939 return dynarray_data(layer->ids);
942 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
945 trace_assert(filedump);
947 size_t n = dynarray_count(layer->ids);
948 char *ids = dynarray_data(layer->ids);
949 Rect *rects = dynarray_data(layer->rects);
950 Color *colors = dynarray_data(layer->colors);
951 Action *actions = dynarray_data(layer->actions);
953 fprintf(filedump, "%zd\n", n);
954 for (size_t i = 0; i < n; ++i) {
955 fprintf(filedump, "%s %f %f %f %f ",
956 ids + ENTITY_MAX_ID_SIZE * i,
957 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
958 color_hex_to_stream(colors[i], filedump);
960 switch (actions[i].type) {
961 case ACTION_NONE: {} break;
963 case ACTION_TOGGLE_GOAL:
964 case ACTION_HIDE_LABEL: {
965 fprintf(filedump, " %d %.*s",
966 (int)actions[i].type,
967 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
969 case ACTION_N: break;
972 fprintf(filedump, "\n");
978 const Action *rect_layer_actions(const RectLayer *layer)
980 return dynarray_data(layer->actions);