]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/level_editor/rect_layer.c
(#164) Make layer translation units more unity build friendly
[nothing.git] / src / game / level / level_editor / rect_layer.c
index 006fb82fcab247df5d6714aad7e41a00b1ca95d4..757db4d44f97622ff4434237e76e2cd365abe14c 100644 (file)
 #define CREATE_AREA_THRESHOLD 10.0
 #define RECT_LAYER_GRID_ROWS 3
 #define RECT_LAYER_GRID_COLUMNS 4
-#define SNAPPING_THRESHOLD 10.0f
 
-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]
@@ -49,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,
@@ -79,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;
@@ -101,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);
@@ -152,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;
@@ -182,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);
@@ -196,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);
@@ -205,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);
@@ -213,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);
@@ -223,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,                                            \
@@ -258,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));
 
@@ -295,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,
@@ -304,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);
@@ -360,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 &&
@@ -372,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;
@@ -447,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);
@@ -467,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);
@@ -482,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;
@@ -537,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,
@@ -549,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(
@@ -558,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,39 +713,30 @@ static int rect_layer_event_resize(RectLayer *layer,
     return 0;
 }
 
-static inline
-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_rects(size_t ignore_index, Rect *a,
-                Rect *rects, size_t rects_size,
-                float snapping_threshold)
+void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
+                               float snapping_threshold)
 {
-    trace_assert(rects);
     trace_assert(a);
+    trace_assert(layer);
+    trace_assert(layer->selection >= 0);
+
+    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 == ignore_index) continue;
+        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))) {
-            if (fabsf(a->y - b.y) < snapping_threshold)                  a->y = b.y;
-            if (fabsf((a->y + a->h) - b.y) < snapping_threshold)         a->y = b.y - a->h;
-            if (fabsf(a->y - (b.y + b.h)) < snapping_threshold)          a->y = b.y + b.h;
-            if (fabsf((a->y + a->h) - (b.y + b.h)) < snapping_threshold) a->y = b.y + b.h - a->h;
+            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))) {
-            if (fabsf(a->x - b.x) < snapping_threshold)                  a->x = b.x;
-            if (fabsf((a->x + a->w) - b.x) < snapping_threshold)         a->x = b.x - a->w;
-            if (fabsf(a->x - (b.x + b.w)) < snapping_threshold)          a->x = b.x + b.w;
-            if (fabsf((a->x + a->w) - (b.x + b.w)) < snapping_threshold) a->x = b.x + b.w - a->w;
+            snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
         }
     }
 }
@@ -698,11 +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),
-                   SNAPPING_THRESHOLD / camera->scale);
+        snap_rect_move_if_enabled(layer, &layer->inter_rect,
+                                  SNAPPING_THRESHOLD / camera->scale);
     } break;
 
     case SDL_MOUSEBUTTONUP: {
@@ -713,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;
@@ -735,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);
@@ -1050,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;
         }
@@ -1071,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));