X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fgame%2Flevel%2Flevel_editor%2Frect_layer.c;h=757db4d44f97622ff4434237e76e2cd365abe14c;hb=81a40fe9db71c2f1dc89ff14e02fa8bba014d623;hp=03226b5841fd3d4b9acb2e9688fedebd06551d79;hpb=daf7e5ec740e627b44c089cecab343e53767888a;p=nothing.git diff --git a/src/game/level/level_editor/rect_layer.c b/src/game/level/level_editor/rect_layer.c index 03226b58..757db4d4 100644 --- a/src/game/level/level_editor/rect_layer.c +++ b/src/game/level/level_editor/rect_layer.c @@ -25,9 +25,9 @@ #define RECT_LAYER_GRID_ROWS 3 #define RECT_LAYER_GRID_COLUMNS 4 -static int clipboard = 0; -static Rect clipboard_rect; -static Color clipboard_color; +static int rect_clipboard = 0; +static Rect rect_clipboard_rect; +static Color rect_clipboard_color; static Cursor_Style resize_styles[1 << RECT_SIDE_N] = { 0, // [0] @@ -48,8 +48,6 @@ static Cursor_Style resize_styles[1 << RECT_SIDE_N] = { typedef enum { RECT_LAYER_IDLE = 0, RECT_LAYER_CREATE, - // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with - // TODO(#1129): different cursor image in resize mode RECT_LAYER_RESIZE, RECT_LAYER_MOVE, RECT_LAYER_ID_RENAME, @@ -78,18 +76,20 @@ struct RectLayer { const char *id_name_prefix; Grid *grid; Cursor *cursor; + + int snapping_enabled; }; typedef enum { - UNDO_ADD, - UNDO_DELETE, - UNDO_UPDATE, - UNDO_SWAP -} UndoType; + RECT_UNDO_ADD, + RECT_UNDO_DELETE, + RECT_UNDO_UPDATE, + RECT_UNDO_SWAP +} RectUndoType; // Delete, Update typedef struct { - UndoType type; + RectUndoType type; RectLayer *layer; size_t index; Rect rect; @@ -100,47 +100,47 @@ typedef struct { // Add typedef struct { - UndoType type; + RectUndoType type; RectLayer *layer; size_t index; } UndoAddContext; // Swap typedef struct { - UndoType type; + RectUndoType type; RectLayer *layer; size_t index1; size_t index2; } UndoSwapContext; typedef union { - UndoType type; + RectUndoType type; UndoAddContext add; UndoElementContext element; UndoSwapContext swap; -} UndoContext; +} RectUndoContext; static -UndoContext create_undo_add_context(RectLayer *layer, size_t index) +RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index) { trace_assert(layer); trace_assert(index < dynarray_count(layer->rects)); - UndoContext undo_context; - undo_context.add.type = UNDO_ADD; + RectUndoContext undo_context; + undo_context.add.type = RECT_UNDO_ADD; undo_context.add.layer = layer; undo_context.add.index = index; return undo_context; } static -UndoContext create_undo_element_context(RectLayer *layer) +RectUndoContext create_rect_undo_element_context(RectLayer *layer) { trace_assert(layer); size_t index = (size_t) layer->selection; trace_assert(index < dynarray_count(layer->rects)); - UndoContext undo_context; + RectUndoContext undo_context; undo_context.element.layer = layer; undo_context.element.index = index; dynarray_copy_to(layer->rects, &undo_context.element.rect, index); @@ -151,26 +151,26 @@ UndoContext create_undo_element_context(RectLayer *layer) } static -UndoContext create_undo_update_context(RectLayer *rect_layer) +RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer) { - UndoContext undo_context = create_undo_element_context(rect_layer); - undo_context.type = UNDO_UPDATE; + RectUndoContext undo_context = create_rect_undo_element_context(rect_layer); + undo_context.type = RECT_UNDO_UPDATE; return undo_context; } static -UndoContext create_undo_delete_context(RectLayer *rect_layer) +RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer) { - UndoContext undo_context = create_undo_element_context(rect_layer); - undo_context.type = UNDO_DELETE; + RectUndoContext undo_context = create_rect_undo_element_context(rect_layer); + undo_context.type = RECT_UNDO_DELETE; return undo_context; } static -UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2) +RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2) { - UndoContext undo_context; - undo_context.swap.type = UNDO_SWAP; + RectUndoContext undo_context; + undo_context.swap.type = RECT_UNDO_SWAP; undo_context.swap.layer = rect_layer; undo_context.swap.index1 = index1; undo_context.swap.index2 = index2; @@ -181,12 +181,12 @@ static void rect_layer_undo(void *context, size_t context_size) { trace_assert(context); - trace_assert(sizeof(UndoContext) == context_size); + trace_assert(sizeof(RectUndoContext) == context_size); - UndoContext *undo_context = context; + RectUndoContext *undo_context = context; switch (undo_context->type) { - case UNDO_ADD: { + case RECT_UNDO_ADD: { RectLayer *layer = undo_context->add.layer; dynarray_delete_at(layer->rects, undo_context->add.index); dynarray_delete_at(layer->colors, undo_context->add.index); @@ -195,7 +195,7 @@ void rect_layer_undo(void *context, size_t context_size) layer->selection = -1; } break; - case UNDO_DELETE: { + case RECT_UNDO_DELETE: { RectLayer *layer = undo_context->element.layer; dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect); dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color); @@ -204,7 +204,7 @@ void rect_layer_undo(void *context, size_t context_size) layer->selection = -1; } break; - case UNDO_UPDATE: { + case RECT_UNDO_UPDATE: { RectLayer *layer = undo_context->element.layer; dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect); dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color); @@ -212,7 +212,7 @@ void rect_layer_undo(void *context, size_t context_size) dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action); } break; - case UNDO_SWAP: { + case RECT_UNDO_SWAP: { RectLayer *layer = undo_context->element.layer; dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2); dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2); @@ -222,9 +222,9 @@ void rect_layer_undo(void *context, size_t context_size) } } -#define UNDO_PUSH(HISTORY, CONTEXT) \ +#define RECT_UNDO_PUSH(HISTORY, CONTEXT) \ do { \ - UndoContext context = (CONTEXT); \ + RectUndoContext context = (CONTEXT); \ undo_history_push( \ HISTORY, \ rect_layer_undo, \ @@ -257,9 +257,9 @@ static int rect_layer_add_rect(RectLayer *layer, dynarray_push_empty(layer->actions); - UNDO_PUSH( + RECT_UNDO_PUSH( undo_history, - create_undo_add_context( + create_rect_undo_add_context( layer, dynarray_count(layer->rects) - 1)); @@ -294,7 +294,7 @@ static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b, dynarray_swap(layer->ids, a, b); dynarray_swap(layer->actions, a, b); - UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b)); + RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b)); } static int rect_layer_delete_rect_at(RectLayer *layer, @@ -303,7 +303,7 @@ static int rect_layer_delete_rect_at(RectLayer *layer, { trace_assert(layer); - UNDO_PUSH(undo_history, create_undo_delete_context(layer)); + RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer)); dynarray_delete_at(layer->rects, i); dynarray_delete_at(layer->colors, i); @@ -359,6 +359,7 @@ static int rect_layer_event_idle(RectLayer *layer, int rect_at_position = rect_layer_rect_at(layer, position); + Color *colors = dynarray_data(layer->colors); if (layer->selection >= 0 && @@ -371,15 +372,13 @@ static int rect_layer_event_idle(RectLayer *layer, } else if (rect_at_position >= 0) { layer->selection = rect_at_position; layer->state = RECT_LAYER_MOVE; - layer->move_anchor = - vec_sub( - position, - vec( - rects[layer->selection].x, - rects[layer->selection].y)); + layer->move_anchor = vec_sub( + position, + vec( + rects[layer->selection].x, + rects[layer->selection].y)); layer->color_picker = create_color_picker_from_rgba(colors[rect_at_position]); - dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position); } else { layer->selection = rect_at_position; @@ -446,6 +445,11 @@ static int rect_layer_event_idle(RectLayer *layer, } } break; + case SDLK_q: { + // TODO(#1171): there is no UI indication that we are in the snapping mode + layer->snapping_enabled = !layer->snapping_enabled; + } break; + case SDLK_F2: { if (layer->selection >= 0) { const char *ids = dynarray_data(layer->ids); @@ -466,14 +470,14 @@ static int rect_layer_event_idle(RectLayer *layer, case SDLK_c: { if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) { - clipboard = 1; - dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection); - dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection); + rect_clipboard = 1; + dynarray_copy_to(layer->rects, &rect_clipboard_rect, (size_t)layer->selection); + dynarray_copy_to(layer->colors, &rect_clipboard_color, (size_t)layer->selection); } } break; case SDLK_v: { - if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) { + if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) { int x, y; SDL_GetMouseState(&x, &y); Vec2f position = camera_map_screen(camera, x, y); @@ -481,8 +485,8 @@ static int rect_layer_event_idle(RectLayer *layer, rect_layer_add_rect( layer, rect(position.x, position.y, - clipboard_rect.w, clipboard_rect.h), - clipboard_color, + rect_clipboard_rect.w, rect_clipboard_rect.h), + rect_clipboard_color, undo_history); } } break; @@ -536,6 +540,33 @@ static int rect_layer_event_create(RectLayer *layer, return 0; } +static +void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold) +{ + trace_assert(layer); + trace_assert(layer->selection >= 0); + trace_assert(a); + + if (!layer->snapping_enabled) return; + + Rect *rects = dynarray_data(layer->rects); + size_t rects_size = dynarray_count(layer->rects); + + for (size_t i = 0; i < rects_size; ++i) { + if (i == (size_t) layer->selection) continue; + + const Rect b = rects[i]; + + if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) { + snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold); + } + + if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) { + snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold); + } + } +} + static int rect_layer_event_resize(RectLayer *layer, const SDL_Event *event, const Camera *camera, @@ -548,6 +579,8 @@ static int rect_layer_event_resize(RectLayer *layer, Rect *rects = dynarray_data(layer->rects); + float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale; + switch (event->type) { case SDL_MOUSEMOTION: { Vec2f position = camera_map_screen( @@ -557,62 +590,122 @@ static int rect_layer_event_resize(RectLayer *layer, switch (layer->resize_mask) { case 1: { // TOP + Rect a = rect(rects[layer->selection].x, + position.y, + rects[layer->selection].w, + rects[layer->selection].h); + + snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold); + layer->inter_rect = rect_from_points( - vec(rects[layer->selection].x, position.y), + vec(a.x, a.y), rect_position2(rects[layer->selection])); } break; case 2: { // LEFT + Rect a = rect(position.x, + rects[layer->selection].y, + rects[layer->selection].w, + rects[layer->selection].h); + + snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold); + layer->inter_rect = rect_from_points( - vec(position.x, rects[layer->selection].y), + vec(a.x, a.y), rect_position2(rects[layer->selection])); } break; case 3: { // TOP,LEFT + Rect a = rect( + position.x, + position.y, + rects[layer->selection].w, + rects[layer->selection].h); + + snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold); + layer->inter_rect = rect_from_points( - position, + vec(a.x, a.y), rect_position2(rects[layer->selection])); } break; case 4: { // BOTTOM + Rect a = rect(rects[layer->selection].x, + position.y, + rects[layer->selection].w, + rects[layer->selection].h); + + snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold); + layer->inter_rect = rect_from_points( rect_position(rects[layer->selection]), vec(rects[layer->selection].x + rects[layer->selection].w, - position.y)); + a.y)); } break; case 6: { // BOTTOM,LEFT + Rect a = rect( + position.x, + position.y, + rects[layer->selection].w, + -rects[layer->selection].h); + + snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold); + layer->inter_rect = rect_from_points( - vec(position.x, rects[layer->selection].y), + vec(a.x, rects[layer->selection].y), vec(rects[layer->selection].x + rects[layer->selection].w, - position.y)); + a.y)); } break; case 8: { // RIGHT + Rect a = rect(position.x, + rects[layer->selection].y, + rects[layer->selection].w, + rects[layer->selection].h); + + snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold); + layer->inter_rect = rect_from_points( rect_position(rects[layer->selection]), - vec(position.x, - rects[layer->selection].y + rects[layer->selection].h)); + vec(a.x, rects[layer->selection].y + rects[layer->selection].h)); } break; case 9: { // TOP,RIGHT + Rect a = rect( + position.x, + position.y, + -rects[layer->selection].w, + rects[layer->selection].h); + + snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold); + layer->inter_rect = rect_from_points( - vec(rects[layer->selection].x, position.y), - vec(position.x, + vec(rects[layer->selection].x, a.y), + vec(a.x, rects[layer->selection].y + rects[layer->selection].h)); } break; case 12: { // BOTTOM,RIGHT + Rect a = rect( + position.x, + position.y, + -rects[layer->selection].w, + -rects[layer->selection].h); + + snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold); + layer->inter_rect = rect_from_points( rect_position(rects[layer->selection]), - position); + vec(a.x, a.y)); } break; } + } break; case SDL_MOUSEBUTTONUP: { layer->state = RECT_LAYER_IDLE; - UNDO_PUSH(undo_history, create_undo_update_context(layer)); + RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer)); dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect); } break; } @@ -621,93 +714,30 @@ static int rect_layer_event_resize(RectLayer *layer, } static -int segment_overlap(Vec2f a, Vec2f b) -{ - trace_assert(a.x <= a.y); - trace_assert(b.x <= b.y); - return a.y >= b.x && b.y >= a.x; -} - -static -void snap_rect(Rect *a, Rect b) +void snap_rect_move_if_enabled(RectLayer *layer, Rect *a, + float snapping_threshold) { trace_assert(a); + trace_assert(layer); + trace_assert(layer->selection >= 0); -#define SNAPPING_THRESHOLD 10.0f - - for (Rect_side a_side = 0; a_side < RECT_SIDE_N; ++a_side) { - for (Rect_side b_side = 0; b_side < RECT_SIDE_N; ++b_side) { - if (a_side == RECT_SIDE_BOTTOM && - b_side == RECT_SIDE_TOP && - segment_overlap( - vec(a->x, a->x + a->w), - vec(b.x, b.x + b.w)) && - fabsf((a->y + a->h) - b.y) < SNAPPING_THRESHOLD) { - a->y = b.y - a->h; - } else if (a_side == RECT_SIDE_TOP && - b_side == RECT_SIDE_TOP && - segment_overlap( - vec(a->x, a->x + a->w), - vec(b.x, b.x + b.w)) && - fabsf(a->y - b.y) < SNAPPING_THRESHOLD) { - a->y = b.y; - } else if (a_side == RECT_SIDE_LEFT && - b_side == RECT_SIDE_RIGHT && - segment_overlap( - vec(a->y, a->y + a->h), - vec(b.y, b.y + b.h)) && - fabsf(a->x - (b.x + b.w)) < SNAPPING_THRESHOLD) { - a->x = b.x + b.w; - } else if (a_side == RECT_SIDE_RIGHT && - b_side == RECT_SIDE_RIGHT && - segment_overlap( - vec(a->y, a->y + a->h), - vec(b.y, b.y + b.h)) && - fabsf((a->x + a->w) - (b.x + b.w)) < SNAPPING_THRESHOLD) { - a->x = b.x + b.w - a->w; - } else if (a_side == RECT_SIDE_TOP && - b_side == RECT_SIDE_BOTTOM && - segment_overlap( - vec(a->x, a->x + a->w), - vec(b.x, b.x + b.w)) && - fabsf(a->y - (b.y + b.h)) < SNAPPING_THRESHOLD) { - a->y = b.y + b.h; - } else if (a_side == RECT_SIDE_BOTTOM && - b_side == RECT_SIDE_BOTTOM && - segment_overlap( - vec(a->x, a->x + a->w), - vec(b.x, b.x + b.w)) && - fabsf((a->y + a->h) - (b.y + b.h)) < SNAPPING_THRESHOLD) { - a->y = b.y + b.h - a->h; - } else if (a_side == RECT_SIDE_LEFT && - b_side == RECT_SIDE_RIGHT && - segment_overlap( - vec(a->y, a->y + a->h), - vec(b.y, b.y + b.h)) && - fabs((a->x + a->w) - b.x) < SNAPPING_THRESHOLD) { - a->x = b.x - a->w; - } else if (a_side == RECT_SIDE_LEFT && - b_side == RECT_SIDE_LEFT && - segment_overlap( - vec(a->y, a->y + a->h), - vec(b.y, b.y + b.h)) && - fabs(a->x - b.x) < SNAPPING_THRESHOLD) { - a->x = b.x; - } - } - } -} + if (!layer->snapping_enabled) return; -static -void snap_rects(size_t ignore_index, Rect *rect, - Rect *rects, size_t rects_size) -{ - trace_assert(rects); - trace_assert(rect); + Rect *rects = dynarray_data(layer->rects); + size_t rects_size = dynarray_count(layer->rects); for (size_t i = 0; i < rects_size; ++i) { - if (i == ignore_index) continue; - snap_rect(rect, rects[i]); + if (i == (size_t) layer->selection) continue; + + const Rect b = rects[i]; + + if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) { + snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold); + } + + if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) { + snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold); + } } } @@ -751,10 +781,8 @@ static int rect_layer_event_move(RectLayer *layer, } } - // TODO(#1141): Rect Snapping in Level Editor should be optional - // TODO(#1142): Resize mode of Rect Layer does not support Snapping - snap_rects((size_t) layer->selection, &layer->inter_rect, - rects, dynarray_count(layer->rects)); + snap_rect_move_if_enabled(layer, &layer->inter_rect, + SNAPPING_THRESHOLD / camera->scale); } break; case SDL_MOUSEBUTTONUP: { @@ -765,7 +793,7 @@ static int rect_layer_event_move(RectLayer *layer, rect_position(rects[layer->selection]))); if (distance > 1e-6) { - UNDO_PUSH(undo_history, create_undo_update_context(layer)); + RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer)); dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect); } } break; @@ -787,7 +815,7 @@ static int rect_layer_event_id_rename(RectLayer *layer, case SDL_KEYDOWN: { switch (event->key.keysym.sym) { case SDLK_RETURN: { - UNDO_PUSH(undo_history, create_undo_update_context(layer)); + RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer)); char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection); memset(id, 0, ENTITY_MAX_ID_SIZE); @@ -1102,7 +1130,7 @@ int rect_layer_event_recolor(RectLayer *layer, layer->inter_color = color_picker_rgba(&layer->color_picker); if (!color_picker_drag(&layer->color_picker)) { - UNDO_PUSH(undo_history, create_undo_update_context(layer)); + RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer)); dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color); layer->state = RECT_LAYER_IDLE; } @@ -1123,7 +1151,7 @@ int rect_layer_event(RectLayer *layer, switch (event->type) { case SDL_WINDOWEVENT: { switch (event->window.event) { - case SDL_WINDOWEVENT_RESIZED: { + case SDL_WINDOWEVENT_SIZE_CHANGED: { grid_relayout(layer->grid, rect(0.0f, 0.0f, (float) event->window.data1, (float) event->window.data2));