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)) {
233 create_undo_add_context(
235 dynarray_count(layer->rects) - 1));
240 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
244 int n = (int) dynarray_count(layer->rects);
245 Rect *rects = dynarray_data(layer->rects);
247 for (int i = n - 1; i >= 0; --i) {
248 if (rect_contains_point(rects[i], position)) {
256 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
257 UndoHistory *undo_history)
260 trace_assert(a < dynarray_count(layer->rects));
261 trace_assert(b < dynarray_count(layer->rects));
263 dynarray_swap(layer->rects, a, b);
264 dynarray_swap(layer->colors, a, b);
265 dynarray_swap(layer->ids, a, b);
266 dynarray_swap(layer->actions, a, b);
268 UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b));
271 static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
273 const Rect overlay_rect =
275 camera_rect(camera, boundary_rect),
276 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
279 overlay_rect.x + overlay_rect.w,
280 overlay_rect.y + overlay_rect.h,
281 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
282 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
285 static int rect_layer_delete_rect_at(RectLayer *layer,
287 UndoHistory *undo_history)
291 UNDO_PUSH(undo_history, create_undo_delete_context(layer));
293 dynarray_delete_at(layer->rects, i);
294 dynarray_delete_at(layer->colors, i);
295 dynarray_delete_at(layer->ids, i);
296 dynarray_delete_at(layer->actions, i);
301 static int rect_layer_event_idle(RectLayer *layer,
302 const SDL_Event *event,
303 const Camera *camera,
304 UndoHistory *undo_history)
308 trace_assert(camera);
310 int color_changed = 0;
311 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
316 if (layer->selection >= 0) {
317 dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
318 layer->state = RECT_LAYER_RECOLOR;
323 switch (event->type) {
324 case SDL_MOUSEBUTTONDOWN: {
325 switch (event->button.button) {
326 case SDL_BUTTON_LEFT: {
327 Vec2f position = camera_map_screen(
331 int rect_at_position =
332 rect_layer_rect_at(layer, position);
334 Rect *rects = dynarray_data(layer->rects);
335 Color *colors = dynarray_data(layer->colors);
337 if (layer->selection >= 0 && rect_contains_point(
338 rect_layer_resize_anchor(
340 rects[layer->selection]),
342 (float) event->button.x,
343 (float) event->button.y))) {
344 layer->state = RECT_LAYER_RESIZE;
345 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
346 } else if (rect_at_position >= 0) {
347 layer->selection = rect_at_position;
348 layer->state = RECT_LAYER_MOVE;
353 rects[layer->selection].x,
354 rects[layer->selection].y));
355 layer->color_picker =
356 create_color_picker_from_rgba(colors[rect_at_position]);
358 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
360 layer->selection = rect_at_position;
362 if (layer->selection < 0) {
363 layer->state = RECT_LAYER_CREATE;
364 layer->create_begin = position;
365 layer->create_end = position;
373 switch (event->key.keysym.sym) {
375 if ((event->key.keysym.mod & KMOD_SHIFT)
376 && (layer->selection >= 0)
377 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
378 rect_layer_swap_elements(
380 (size_t) layer->selection,
381 (size_t) layer->selection + 1,
388 if ((event->key.keysym.mod & KMOD_SHIFT)
389 && (layer->selection > 0)
390 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
391 rect_layer_swap_elements(
393 (size_t) layer->selection,
394 (size_t) layer->selection - 1,
401 if (layer->selection >= 0) {
402 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
403 layer->selection = -1;
408 if (layer->selection >= 0) {
409 const char *ids = dynarray_data(layer->ids);
410 Color *colors = dynarray_data(layer->colors);
413 layer->id_edit_field,
414 RECT_LAYER_ID_LABEL_SIZE,
415 color_invert(colors[layer->selection]));
417 layer->state = RECT_LAYER_ID_RENAME;
419 layer->id_edit_field,
420 ids + layer->selection * ENTITY_MAX_ID_SIZE);
421 SDL_StartTextInput();
426 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
428 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
429 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
434 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
436 SDL_GetMouseState(&x, &y);
437 Vec2f position = camera_map_screen(camera, x, y);
441 rect(position.x, position.y,
442 clipboard_rect.w, clipboard_rect.h),
454 static int rect_layer_event_create(RectLayer *layer,
455 const SDL_Event *event,
456 const Camera *camera,
457 UndoHistory *undo_history)
461 trace_assert(camera);
463 switch (event->type) {
464 case SDL_MOUSEBUTTONUP: {
465 switch (event->button.button) {
466 case SDL_BUTTON_LEFT: {
467 const Rect real_rect =
471 const float area = real_rect.w * real_rect.h;
473 if (area >= CREATE_AREA_THRESHOLD) {
477 color_picker_rgba(&layer->color_picker),
480 log_info("The area is too small %f. Such small box won't be created.\n", area);
482 layer->state = RECT_LAYER_IDLE;
487 case SDL_MOUSEMOTION: {
488 layer->create_end = camera_map_screen(
497 static int rect_layer_event_resize(RectLayer *layer,
498 const SDL_Event *event,
499 const Camera *camera,
500 UndoHistory *undo_history)
504 trace_assert(camera);
505 trace_assert(layer->selection >= 0);
507 switch (event->type) {
508 case SDL_MOUSEMOTION: {
509 layer->inter_rect = rect_from_points(
510 vec(layer->inter_rect.x, layer->inter_rect.y),
516 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
517 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
520 case SDL_MOUSEBUTTONUP: {
521 layer->state = RECT_LAYER_IDLE;
522 UNDO_PUSH(undo_history, create_undo_update_context(layer));
523 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
530 static int rect_layer_event_move(RectLayer *layer,
531 const SDL_Event *event,
532 const Camera *camera,
533 UndoHistory *undo_history)
537 trace_assert(camera);
538 trace_assert(layer->selection >= 0);
540 Rect *rects = dynarray_data(layer->rects);
542 switch (event->type) {
543 case SDL_MOUSEMOTION: {
544 Vec2f position = vec_sub(
551 trace_assert(layer->selection >= 0);
553 layer->inter_rect.x = position.x;
554 layer->inter_rect.y = position.y;
557 case SDL_MOUSEBUTTONUP: {
558 layer->state = RECT_LAYER_IDLE;
560 float distance = vec_length(
561 vec_sub(rect_position(layer->inter_rect),
562 rect_position(rects[layer->selection])));
564 if (distance > 1e-6) {
565 UNDO_PUSH(undo_history, create_undo_update_context(layer));
566 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
573 static int rect_layer_event_id_rename(RectLayer *layer,
574 const SDL_Event *event,
575 const Camera *camera,
576 UndoHistory *undo_history)
580 trace_assert(camera);
581 trace_assert(layer->selection >= 0);
583 switch (event->type) {
585 switch (event->key.keysym.sym) {
587 UNDO_PUSH(undo_history, create_undo_update_context(layer));
589 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
590 memset(id, 0, ENTITY_MAX_ID_SIZE);
591 memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
592 layer->state = RECT_LAYER_IDLE;
597 layer->state = RECT_LAYER_IDLE;
604 return edit_field_event(layer->id_edit_field, event);
607 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
616 RectLayer *create_rect_layer(const char *id_name_prefix)
618 Lt *lt = create_lt();
620 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
626 layer->ids = PUSH_LT(
628 create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
630 if (layer->ids == NULL) {
634 layer->rects = PUSH_LT(
636 create_dynarray(sizeof(Rect)),
638 if (layer->rects == NULL) {
642 layer->colors = PUSH_LT(
644 create_dynarray(sizeof(Color)),
646 if (layer->colors == NULL) {
650 layer->actions = PUSH_LT(
652 create_dynarray(sizeof(Action)),
654 if (layer->actions == NULL) {
658 layer->id_edit_field = PUSH_LT(
661 RECT_LAYER_ID_LABEL_SIZE,
664 if (layer->id_edit_field == NULL) {
668 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
669 layer->selection = -1;
670 layer->id_name_prefix = id_name_prefix;
675 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
677 trace_assert(line_stream);
679 RectLayer *layer = create_rect_layer(id_name_prefix);
684 const char *line = line_stream_next(line_stream);
686 RETURN_LT(layer->lt, NULL);
690 if (sscanf(line, "%zu", &count) < 0) {
691 RETURN_LT(layer->lt, NULL);
694 for (size_t i = 0; i < count; ++i) {
695 line = line_stream_next(line_stream);
697 RETURN_LT(layer->lt, NULL);
702 char id[ENTITY_MAX_ID_SIZE];
706 "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
711 log_fail("%s\n", strerror(errno));
712 RETURN_LT(layer->lt, NULL);
716 Color color = hexstr(hex);
717 dynarray_push(layer->rects, &rect);
718 dynarray_push(layer->ids, id);
719 dynarray_push(layer->colors, &color);
723 if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
725 switch (action.type) {
726 case ACTION_NONE: break;
728 case ACTION_TOGGLE_GOAL:
729 case ACTION_HIDE_LABEL: {
730 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
731 log_fail("%s\n", strerror(errno));
732 RETURN_LT(layer->lt, NULL);
738 dynarray_push(layer->actions, &action);
744 void destroy_rect_layer(RectLayer *layer)
747 RETURN_LT0(layer->lt);
750 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
753 trace_assert(camera);
755 const size_t n = dynarray_count(layer->rects);
756 Rect *rects = dynarray_data(layer->rects);
757 Color *colors = dynarray_data(layer->colors);
758 const char *ids = dynarray_data(layer->ids);
761 for (size_t i = 0; i < n; ++i) {
762 Rect rect = rects[i];
763 Color color = colors[i];
765 if (layer->selection == (int) i) {
766 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
767 rect = layer->inter_rect;
770 if (layer->state == RECT_LAYER_RECOLOR) {
771 color = layer->inter_color;
775 if (camera_fill_rect(
780 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
785 if (active && (size_t) layer->selection == i) {
786 const Rect overlay_rect =
788 camera_rect(camera, rect),
789 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
790 const Color overlay_color = color_invert(color);
793 if (camera_fill_rect(
798 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
802 if (camera_draw_thicc_rect_screen(
806 RECT_LAYER_SELECTION_THICCNESS) < 0) {
811 if (layer->state == RECT_LAYER_ID_RENAME) {
812 // ID renaming Edit Field
813 if (edit_field_render_world(
814 layer->id_edit_field,
816 rect_position(rect)) < 0) {
821 if (camera_render_text(
823 ids + layer->selection * ENTITY_MAX_ID_SIZE,
824 RECT_LAYER_ID_LABEL_SIZE,
826 rect_position(rect)) < 0) {
832 if (camera_fill_rect_screen(
834 rect_layer_resize_anchor(camera, rect),
835 overlay_color) < 0) {
842 const Color color = color_picker_rgba(&layer->color_picker);
843 if (layer->state == RECT_LAYER_CREATE) {
844 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
849 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
857 int rect_layer_event_recolor(RectLayer *layer,
858 const SDL_Event *event,
859 const Camera *camera,
860 UndoHistory *undo_history)
864 trace_assert(camera);
865 trace_assert(undo_history);
866 trace_assert(layer->selection >= 0);
868 int color_changed = 0;
869 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
874 layer->inter_color = color_picker_rgba(&layer->color_picker);
876 if (!color_picker_drag(&layer->color_picker)) {
877 UNDO_PUSH(undo_history, create_undo_update_context(layer));
878 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
879 layer->state = RECT_LAYER_IDLE;
886 int rect_layer_event(RectLayer *layer,
887 const SDL_Event *event,
888 const Camera *camera,
889 UndoHistory *undo_history)
893 trace_assert(undo_history);
895 switch (layer->state) {
896 case RECT_LAYER_IDLE:
897 return rect_layer_event_idle(layer, event, camera, undo_history);
899 case RECT_LAYER_CREATE:
900 return rect_layer_event_create(layer, event, camera, undo_history);
902 case RECT_LAYER_RESIZE:
903 return rect_layer_event_resize(layer, event, camera, undo_history);
905 case RECT_LAYER_MOVE:
906 return rect_layer_event_move(layer, event, camera, undo_history);
908 case RECT_LAYER_ID_RENAME:
909 return rect_layer_event_id_rename(layer, event, camera, undo_history);
911 case RECT_LAYER_RECOLOR:
912 return rect_layer_event_recolor(layer, event, camera, undo_history);
918 size_t rect_layer_count(const RectLayer *layer)
920 return dynarray_count(layer->rects);
923 const Rect *rect_layer_rects(const RectLayer *layer)
925 return dynarray_data(layer->rects);
928 const Color *rect_layer_colors(const RectLayer *layer)
930 return dynarray_data(layer->colors);
933 const char *rect_layer_ids(const RectLayer *layer)
935 return dynarray_data(layer->ids);
938 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
941 trace_assert(filedump);
943 size_t n = dynarray_count(layer->ids);
944 char *ids = dynarray_data(layer->ids);
945 Rect *rects = dynarray_data(layer->rects);
946 Color *colors = dynarray_data(layer->colors);
947 Action *actions = dynarray_data(layer->actions);
949 fprintf(filedump, "%zd\n", n);
950 for (size_t i = 0; i < n; ++i) {
951 fprintf(filedump, "%s %f %f %f %f ",
952 ids + ENTITY_MAX_ID_SIZE * i,
953 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
954 color_hex_to_stream(colors[i], filedump);
956 switch (actions[i].type) {
957 case ACTION_NONE: {} break;
959 case ACTION_TOGGLE_GOAL:
960 case ACTION_HIDE_LABEL: {
961 fprintf(filedump, " %d %.*s",
962 (int)actions[i].type,
963 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
967 fprintf(filedump, "\n");
973 const Action *rect_layer_actions(const RectLayer *layer)
975 return dynarray_data(layer->actions);