]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/level_editor/point_layer.c
Revert "(#164) %z -> %l for mingw on Windows"
[nothing.git] / src / game / level / level_editor / point_layer.c
index e50559c7ecb01063e19147f048b0a69fa6d4aa75..50f6d77535ddb40a7145cc78f94bf0babba691a2 100644 (file)
 #define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
 #define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
 
-static int clipboard = 0;
-static Color clipboard_color;
+static int point_clipboard = 0;
+static Color point_clipboard_color;
+
+// TODO(#1140): PointLayer does not support snapping
 
 typedef enum {
     POINT_LAYER_IDLE = 0,
@@ -35,13 +37,13 @@ struct PointLayer
 {
     Lt *lt;
     PointLayerState state;
-    Dynarray/*<Point>*/ *positions;
+    Dynarray/*<Vec2f>*/ *positions;
     Dynarray/*<Color>*/ *colors;
     Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
-    int selected;
+    int selection;
     ColorPicker color_picker;
 
-    Point inter_position;
+    Vec2f inter_position;
     Color inter_color;
     Edit_field *edit_field;
 
@@ -50,30 +52,52 @@ struct PointLayer
 };
 
 typedef enum {
-    UNDO_ADD,
-    UNDO_DELETE,
-    UNDO_UPDATE
-} UndoType;
+    POINT_UNDO_ADD,
+    POINT_UNDO_DELETE,
+    POINT_UNDO_UPDATE,
+    POINT_UNDO_SWAP
+} PointUndoType;
 
 typedef struct {
-    UndoType type;
+    PointUndoType type;
     PointLayer *layer;
-    Point position;
+    Vec2f position;
     Color color;
     char id[ID_MAX_SIZE];
     size_t index;
-} UndoContext;
+    size_t index2;
+} PointUndoContext;
 
 static
-UndoContext point_layer_create_undo_context(PointLayer *point_layer,
-                                            UndoType type)
+PointUndoContext create_point_undo_swap_context(PointLayer *point_layer,
+                                     size_t index, size_t index2)
 {
-    UndoContext undo_context;
+    trace_assert(point_layer);
+    trace_assert(index < dynarray_count(point_layer->positions));
+    trace_assert(index2 < dynarray_count(point_layer->positions));
+
+    PointUndoContext undo_context;
+    undo_context.type = POINT_UNDO_SWAP;
+    undo_context.layer = point_layer;
+    undo_context.index = index;
+    undo_context.index2 = index2;
+    return undo_context;
+}
+
+static
+PointUndoContext create_point_undo_context(PointLayer *point_layer,
+                                PointUndoType type)
+{
+    trace_assert(type != POINT_UNDO_SWAP);
+
+    (void) create_point_undo_swap_context;
+
+    PointUndoContext undo_context;
 
     size_t index =
-        type == UNDO_ADD
+        type == POINT_UNDO_ADD
         ? dynarray_count(point_layer->positions) - 1
-        : (size_t) point_layer->selected;
+        : (size_t) point_layer->selection;
 
     undo_context.type = type;
     undo_context.layer = point_layer;
@@ -89,37 +113,43 @@ static
 void point_layer_undo(void *context, size_t context_size)
 {
     trace_assert(context);
-    trace_assert(sizeof(UndoContext) == context_size);
+    trace_assert(sizeof(PointUndoContext) == context_size);
 
-    UndoContext *undo_context = context;
+    PointUndoContext *undo_context = context;
     PointLayer *point_layer = undo_context->layer;
 
     switch (undo_context->type) {
-    case UNDO_ADD: {
+    case POINT_UNDO_ADD: {
         dynarray_pop(point_layer->positions, NULL);
         dynarray_pop(point_layer->colors, NULL);
         dynarray_pop(point_layer->ids, NULL);
-        point_layer->selected = -1;
+        point_layer->selection = -1;
     } break;
 
-    case UNDO_DELETE: {
+    case POINT_UNDO_DELETE: {
         dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position);
         dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color);
         dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id);
-        point_layer->selected = -1;
+        point_layer->selection = -1;
     } break;
 
-    case UNDO_UPDATE: {
+    case POINT_UNDO_UPDATE: {
         dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position);
         dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color);
         dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id);
     } break;
+
+    case POINT_UNDO_SWAP: {
+        dynarray_swap(point_layer->positions, undo_context->index, undo_context->index2);
+        dynarray_swap(point_layer->colors, undo_context->index, undo_context->index2);
+        dynarray_swap(point_layer->ids, undo_context->index, undo_context->index2);
+    } break;
     }
 }
 
-#define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE)                            \
+#define POINT_UNDO_PUSH(HISTORY, CONTEXT)                                     \
     do {                                                                \
-        UndoContext context = point_layer_create_undo_context(LAYER, UNDO_TYPE); \
+        PointUndoContext context = (CONTEXT);                                \
         undo_history_push(                                              \
             HISTORY,                                                    \
             point_layer_undo,                                           \
@@ -148,7 +178,7 @@ PointLayer *create_point_layer(const char *id_name_prefix)
 
     point_layer->state = POINT_LAYER_IDLE;
 
-    point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
+    point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Vec2f)), destroy_dynarray);
     if (point_layer->positions == NULL) {
         RETURN_LT(lt, NULL);
     }
@@ -206,14 +236,14 @@ PointLayer *create_point_layer_from_line_stream(LineStream *line_stream,
             RETURN_LT(point_layer->lt, NULL);
         }
         const Color color = hexstr(color_name);
-        const Point point = vec(x, y);
+        const Vec2f point = vec(x, y);
 
         dynarray_push(point_layer->colors, &color);
         dynarray_push(point_layer->positions, &point);
         dynarray_push(point_layer->ids, id);
     }
 
-    point_layer->selected = -1;
+    point_layer->selection = -1;
 
     point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
 
@@ -227,7 +257,7 @@ void destroy_point_layer(PointLayer *point_layer)
 }
 
 static inline
-Triangle element_shape(Point position, float scale)
+Triangle element_shape(Vec2f position, float scale)
 {
     return triangle_mat3x3_product(
         equilateral_triangle(),
@@ -244,24 +274,24 @@ int point_layer_render(const PointLayer *point_layer,
     trace_assert(camera);
 
     const int n = (int) dynarray_count(point_layer->positions);
-    Point *positions = dynarray_data(point_layer->positions);
+    Vec2f *positions = dynarray_data(point_layer->positions);
     Color *colors = dynarray_data(point_layer->colors);
     char *ids = dynarray_data(point_layer->ids);
 
     for (int i = 0; i < n; ++i) {
         const Color color = color_scale(
-            point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selected
+            point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
             ? point_layer->inter_color
             : colors[i],
             rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
 
-        const Point position =
-            point_layer->state == POINT_LAYER_MOVE && i == point_layer->selected
+        const Vec2f position =
+            point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
             ? point_layer->inter_position
             : positions[i];
 
         // Selection Layer
-        if (active && i == point_layer->selected) {
+        if (active && i == point_layer->selection) {
             if (camera_fill_triangle(
                     camera,
                     element_shape(
@@ -296,7 +326,7 @@ int point_layer_render(const PointLayer *point_layer,
         if (edit_field_render_world(
                 point_layer->edit_field,
                 camera,
-                positions[point_layer->selected]) < 0) {
+                positions[point_layer->selection]) < 0) {
             return -1;
         }
     }
@@ -311,14 +341,14 @@ int point_layer_render(const PointLayer *point_layer,
 
 static
 int point_layer_element_at(const PointLayer *point_layer,
-                           Point position)
+                           Vec2f position)
 {
     trace_assert(point_layer);
 
     int n = (int) dynarray_count(point_layer->positions);
-    Point *positions = dynarray_data(point_layer->positions);
+    Vec2f *positions = dynarray_data(point_layer->positions);
 
-    for (int i = 0; i < n; ++i) {
+    for (int i = n - 1; i >= 0; --i) {
         if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
             return i;
         }
@@ -329,7 +359,7 @@ int point_layer_element_at(const PointLayer *point_layer,
 
 static
 int point_layer_add_element(PointLayer *point_layer,
-                            Point position,
+                            Vec2f position,
                             Color color,
                             UndoHistory *undo_history)
 {
@@ -345,11 +375,32 @@ int point_layer_add_element(PointLayer *point_layer,
     dynarray_push(point_layer->colors, &color);
     dynarray_push(point_layer->ids, id);
 
-    UNDO_PUSH(point_layer, undo_history, UNDO_ADD);
+    POINT_UNDO_PUSH(
+        undo_history,
+        create_point_undo_context(point_layer, POINT_UNDO_ADD));
 
     return 0;
 }
 
+static
+void point_layer_swap_elements(PointLayer *point_layer,
+                               size_t a, size_t b,
+                               UndoHistory *undo_history)
+{
+    trace_assert(point_layer);
+    trace_assert(undo_history);
+    trace_assert(a < dynarray_count(point_layer->positions));
+    trace_assert(b < dynarray_count(point_layer->positions));
+
+    dynarray_swap(point_layer->positions, a, b);
+    dynarray_swap(point_layer->colors, a, b);
+    dynarray_swap(point_layer->ids, a, b);
+
+    POINT_UNDO_PUSH(
+        undo_history,
+        create_point_undo_swap_context(point_layer, a, b));
+}
+
 static
 void point_layer_delete_nth_element(PointLayer *point_layer,
                                     size_t i,
@@ -357,7 +408,11 @@ void point_layer_delete_nth_element(PointLayer *point_layer,
 {
     trace_assert(point_layer);
 
-    UNDO_PUSH(point_layer, undo_history, UNDO_DELETE);
+    POINT_UNDO_PUSH(
+        undo_history,
+        create_point_undo_context(
+            point_layer,
+            POINT_UNDO_DELETE));
 
     dynarray_delete_at(point_layer->positions, i);
     dynarray_delete_at(point_layer->colors, i);
@@ -384,7 +439,7 @@ int point_layer_idle_event(PointLayer *point_layer,
     }
 
     if (selected) {
-        if (point_layer->selected >= 0) {
+        if (point_layer->selection >= 0) {
             point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
             point_layer->state = POINT_LAYER_RECOLOR;
         }
@@ -395,12 +450,12 @@ int point_layer_idle_event(PointLayer *point_layer,
     case SDL_MOUSEBUTTONDOWN: {
         switch (event->button.button) {
         case SDL_BUTTON_LEFT: {
-            const Point position = camera_map_screen(camera, event->button.x, event->button.y);
+            const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
 
-            point_layer->selected = point_layer_element_at(
+            point_layer->selection = point_layer_element_at(
                 point_layer, position);
 
-            if (point_layer->selected < 0) {
+            if (point_layer->selection < 0) {
                 point_layer_add_element(
                     point_layer,
                     position,
@@ -408,12 +463,12 @@ int point_layer_idle_event(PointLayer *point_layer,
                     undo_history);
             } else {
                 Color *colors = dynarray_data(point_layer->colors);
-                Point *positions = dynarray_data(point_layer->positions);
+                Vec2f *positions = dynarray_data(point_layer->positions);
 
                 point_layer->state = POINT_LAYER_MOVE;
                 point_layer->color_picker =
-                    create_color_picker_from_rgba(colors[point_layer->selected]);
-                point_layer->inter_position = positions[point_layer->selected];
+                    create_color_picker_from_rgba(colors[point_layer->selection]);
+                point_layer->inter_position = positions[point_layer->selection];
             }
         } break;
         }
@@ -421,44 +476,70 @@ int point_layer_idle_event(PointLayer *point_layer,
 
     case SDL_KEYDOWN: {
         switch (event->key.keysym.sym) {
+        case SDLK_UP: {
+            if ((event->key.keysym.mod & KMOD_SHIFT)
+                && (point_layer->selection >= 0)
+                && ((size_t)(point_layer->selection + 1) < dynarray_count(point_layer->positions))) {
+                point_layer_swap_elements(
+                    point_layer,
+                    (size_t) point_layer->selection,
+                    (size_t) point_layer->selection + 1,
+                    undo_history);
+                point_layer->selection++;
+            }
+        } break;
+
+        case SDLK_DOWN: {
+            if ((event->key.keysym.mod & KMOD_SHIFT)
+                && (point_layer->selection > 0)
+                && ((size_t) point_layer->selection < dynarray_count(point_layer->positions))) {
+                point_layer_swap_elements(
+                    point_layer,
+                    (size_t) point_layer->selection,
+                    (size_t) point_layer->selection - 1,
+                    undo_history);
+                point_layer->selection--;
+            }
+        } break;
+
         case SDLK_DELETE: {
-            if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
+            if (0 <= point_layer->selection && point_layer->selection < (int) dynarray_count(point_layer->positions)) {
                 point_layer_delete_nth_element(
                     point_layer,
-                    (size_t)point_layer->selected,
+                    (size_t)point_layer->selection,
                     undo_history);
-                point_layer->selected = -1;
+                point_layer->selection = -1;
             }
         } break;
 
         case SDLK_F2: {
-            if (point_layer->selected >= 0) {
+            if (point_layer->selection >= 0) {
                 char *ids = dynarray_data(point_layer->ids);
                 point_layer->state = POINT_LAYER_EDIT_ID;
                 edit_field_replace(
                     point_layer->edit_field,
-                    ids + ID_MAX_SIZE * point_layer->selected);
+                    ids + ID_MAX_SIZE * point_layer->selection);
                 SDL_StartTextInput();
             }
         } break;
 
         case SDLK_c: {
-            if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selected >= 0) {
-                clipboard = 1;
-                dynarray_copy_to(point_layer->colors, &clipboard_color, (size_t)point_layer->selected);
+            if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
+                point_clipboard = 1;
+                dynarray_copy_to(point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
             }
         } break;
 
         case SDLK_v: {
-            if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
+            if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
                 int x, y;
                 SDL_GetMouseState(&x, &y);
-                Point position = camera_map_screen(camera, x, y);
+                Vec2f position = camera_map_screen(camera, x, y);
 
                 point_layer_add_element(
                     point_layer,
                     position,
-                    clipboard_color,
+                    point_clipboard_color,
                     undo_history);
             }
         } break;
@@ -483,9 +564,13 @@ int point_layer_edit_id_event(PointLayer *point_layer,
     case SDL_KEYDOWN: {
         switch(event->key.keysym.sym) {
         case SDLK_RETURN: {
-            UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
+            POINT_UNDO_PUSH(
+                undo_history,
+                create_point_undo_context(
+                    point_layer,
+                    POINT_UNDO_UPDATE));
 
-            char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
+            char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selection);
             const char *text = edit_field_as_text(point_layer->edit_field);
             size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
             memcpy(id, text, n);
@@ -517,7 +602,9 @@ int point_layer_move_event(PointLayer *point_layer,
     trace_assert(point_layer);
     trace_assert(event);
     trace_assert(camera);
-    trace_assert(point_layer->selected >= 0);
+    trace_assert(point_layer->selection >= 0);
+
+    Vec2f *positions = dynarray_data(point_layer->positions);
 
     switch (event->type) {
     case SDL_MOUSEBUTTONUP: {
@@ -525,20 +612,43 @@ int point_layer_move_event(PointLayer *point_layer,
         case SDL_BUTTON_LEFT: {
             point_layer->state = POINT_LAYER_IDLE;
 
-            // TODO(#1014): just click (without moving) on the point creates an undo history entry
-            UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
-
-            dynarray_replace_at(
-                point_layer->positions,
-                (size_t) point_layer->selected,
-                &point_layer->inter_position);
+            const float distance = vec_length(
+                vec_sub(point_layer->inter_position,
+                        positions[point_layer->selection]));
+
+            if (distance > 1e-6) {
+                POINT_UNDO_PUSH(
+                    undo_history,
+                    create_point_undo_context(
+                        point_layer,
+                        POINT_UNDO_UPDATE));
+
+                dynarray_replace_at(
+                    point_layer->positions,
+                    (size_t) point_layer->selection,
+                    &point_layer->inter_position);
+            }
         } break;
         }
     } break;
 
     case SDL_MOUSEMOTION: {
-        point_layer->inter_position =
-            camera_map_screen(camera, event->motion.x, event->motion.y);
+        const Uint8 *state = SDL_GetKeyboardState(NULL);
+        const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
+        const Vec2f point_pos = positions[point_layer->selection];
+
+        if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
+            point_layer->inter_position = mouse_pos;
+        } else {
+            const float dx = fabsf(point_pos.x - mouse_pos.x);
+            const float dy = fabsf(point_pos.y - mouse_pos.y);
+
+            if (dx > dy) {
+                point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
+            } else {
+                point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
+            }
+        }
     } break;
     }
 
@@ -555,7 +665,7 @@ int point_layer_recolor_event(PointLayer *point_layer,
     trace_assert(event);
     trace_assert(camera);
     trace_assert(undo_history);
-    trace_assert(point_layer->selected >= 0);
+    trace_assert(point_layer->selection >= 0);
 
     int selected = 0;
     if (color_picker_event(
@@ -570,11 +680,15 @@ int point_layer_recolor_event(PointLayer *point_layer,
         point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
 
         if (!color_picker_drag(&point_layer->color_picker)) {
-            UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
+            POINT_UNDO_PUSH(
+                undo_history,
+                create_point_undo_context(
+                    point_layer,
+                    POINT_UNDO_UPDATE));
 
             dynarray_replace_at(
                 point_layer->colors,
-                (size_t) point_layer->selected,
+                (size_t) point_layer->selection,
                 &point_layer->inter_color);
 
             point_layer->state = POINT_LAYER_IDLE;
@@ -618,7 +732,7 @@ size_t point_layer_count(const PointLayer *point_layer)
     return dynarray_count(point_layer->positions);
 }
 
-const Point *point_layer_positions(const PointLayer *point_layer)
+const Vec2f *point_layer_positions(const PointLayer *point_layer)
 {
     trace_assert(point_layer);
     return dynarray_data(point_layer->positions);
@@ -644,7 +758,7 @@ int point_layer_dump_stream(const PointLayer *point_layer,
 
     size_t n = dynarray_count(point_layer->ids);
     char *ids = dynarray_data(point_layer->ids);
-    Point *positions = dynarray_data(point_layer->positions);
+    Vec2f *positions = dynarray_data(point_layer->positions);
     Color *colors = dynarray_data(point_layer->colors);
 
     fprintf(filedump, "%zd\n", n);