]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/level_editor/label_layer.c
(#1212) Use newline for printing debug details in rigid bodies
[nothing.git] / src / game / level / level_editor / label_layer.c
index a3624d02daf33f0f5dc518dd06b8009b6a1ad896..739c2dae1995cebdd8f42ed0e975ca5f9a5d0ad7 100644 (file)
 #include "color_picker.h"
 #include "ui/edit_field.h"
 #include "math/extrema.h"
+#include "config.h"
 
 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
 
-// TODO: Label Layer does not support snapping
+// TODO(#1139): Label Layer does not support snapping
 
 typedef enum {
     LABEL_LAYER_IDLE = 0,
@@ -29,17 +30,17 @@ typedef enum {
     LABEL_LAYER_RECOLOR
 } LabelLayerState;
 
-static int clipboard;
-static char clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
-static Color clipboard_color;
+static int label_clipboard = 0;
+static char label_clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
+static Color label_clipboard_color;
 
 struct LabelLayer {
     Lt *lt;
     LabelLayerState state;
-    Dynarray *ids;
-    Dynarray *positions;
-    Dynarray *colors;
-    Dynarray *texts;
+    Dynarray ids;
+    Dynarray positions;
+    Dynarray colors;
+    Dynarray texts;
     int selection;
     ColorPicker color_picker;
     Vec2f move_anchor;
@@ -51,14 +52,14 @@ struct LabelLayer {
 };
 
 typedef enum {
-    UNDO_ADD,
-    UNDO_DELETE,
-    UNDO_UPDATE,
-    UNDO_SWAP
-} UndoType;
+    LABEL_UNDO_ADD,
+    LABEL_UNDO_DELETE,
+    LABEL_UNDO_UPDATE,
+    LABEL_UNDO_SWAP
+} LabelUndoType;
 
 typedef struct {
-    UndoType type;
+    LabelUndoType type;
     LabelLayer *layer;
     char id[LABEL_LAYER_ID_MAX_SIZE];
     Vec2f position;
@@ -66,18 +67,18 @@ typedef struct {
     char text[LABEL_LAYER_TEXT_MAX_SIZE];
     size_t index;
     size_t index2;
-} UndoContext;
+} LabelUndoContext;
 
 static
-UndoContext create_undo_swap_context(LabelLayer *label_layer,
+LabelUndoContext create_label_undo_swap_context(LabelLayer *label_layer,
                                      size_t index, size_t index2)
 {
     trace_assert(label_layer);
-    trace_assert(index < dynarray_count(label_layer->positions));
-    trace_assert(index2 < dynarray_count(label_layer->positions));
+    trace_assert(index < label_layer->positions.count);
+    trace_assert(index2 < label_layer->positions.count);
 
-    UndoContext undo_context;
-    undo_context.type = UNDO_SWAP;
+    LabelUndoContext undo_context;
+    undo_context.type = LABEL_UNDO_SWAP;
     undo_context.layer = label_layer;
     undo_context.index = index;
     undo_context.index2 = index2;
@@ -85,23 +86,23 @@ UndoContext create_undo_swap_context(LabelLayer *label_layer,
 }
 
 static
-UndoContext create_undo_context(LabelLayer *label_layer, UndoType type)
+LabelUndoContext create_label_undo_context(LabelLayer *label_layer, LabelUndoType type)
 {
     trace_assert(label_layer);
-    trace_assert(type != UNDO_SWAP);
+    trace_assert(type != LABEL_UNDO_SWAP);
 
-    UndoContext undo_context;
+    LabelUndoContext undo_context;
 
-    size_t index = type == UNDO_ADD
-        ? dynarray_count(label_layer->positions) - 1
+    size_t index = type == LABEL_UNDO_ADD
+        ? label_layer->positions.count - 1
         : (size_t)label_layer->selection;
 
     undo_context.type = type;
     undo_context.layer = label_layer;
-    dynarray_copy_to(label_layer->ids, &undo_context.id, index);
-    dynarray_copy_to(label_layer->positions, &undo_context.position, index);
-    dynarray_copy_to(label_layer->colors, &undo_context.color, index);
-    dynarray_copy_to(label_layer->texts, &undo_context.text, index);
+    dynarray_copy_to(&label_layer->ids, &undo_context.id, index);
+    dynarray_copy_to(&label_layer->positions, &undo_context.position, index);
+    dynarray_copy_to(&label_layer->colors, &undo_context.color, index);
+    dynarray_copy_to(&label_layer->texts, &undo_context.text, index);
     undo_context.index = index;
 
     return undo_context;
@@ -111,45 +112,45 @@ static
 void label_layer_undo(void *context, size_t context_size)
 {
     trace_assert(context);
-    trace_assert(sizeof(UndoContext) == context_size);
+    trace_assert(sizeof(LabelUndoContext) == context_size);
 
-    UndoContext *undo_context = context;
+    LabelUndoContext *undo_context = context;
     LabelLayer *label_layer = undo_context->layer;
 
     switch (undo_context->type) {
-    case UNDO_ADD: {
-        dynarray_delete_at(label_layer->ids, undo_context->index);
-        dynarray_delete_at(label_layer->positions, undo_context->index);
-        dynarray_delete_at(label_layer->colors, undo_context->index);
-        dynarray_delete_at(label_layer->texts, undo_context->index);
+    case LABEL_UNDO_ADD: {
+        dynarray_delete_at(&label_layer->ids, undo_context->index);
+        dynarray_delete_at(&label_layer->positions, undo_context->index);
+        dynarray_delete_at(&label_layer->colors, undo_context->index);
+        dynarray_delete_at(&label_layer->texts, undo_context->index);
     } break;
 
-    case UNDO_DELETE: {
-        dynarray_insert_before(label_layer->ids, undo_context->index, &undo_context->id);
-        dynarray_insert_before(label_layer->positions, undo_context->index, &undo_context->position);
-        dynarray_insert_before(label_layer->colors, undo_context->index, &undo_context->color);
-        dynarray_insert_before(label_layer->texts, undo_context->index, &undo_context->text);
+    case LABEL_UNDO_DELETE: {
+        dynarray_insert_before(&label_layer->ids, undo_context->index, &undo_context->id);
+        dynarray_insert_before(&label_layer->positions, undo_context->index, &undo_context->position);
+        dynarray_insert_before(&label_layer->colors, undo_context->index, &undo_context->color);
+        dynarray_insert_before(&label_layer->texts, undo_context->index, &undo_context->text);
     } break;
 
-    case UNDO_UPDATE: {
-        dynarray_replace_at(label_layer->ids, undo_context->index, &undo_context->id);
-        dynarray_replace_at(label_layer->positions, undo_context->index, &undo_context->position);
-        dynarray_replace_at(label_layer->colors, undo_context->index, &undo_context->color);
-        dynarray_replace_at(label_layer->texts, undo_context->index, &undo_context->text);
+    case LABEL_UNDO_UPDATE: {
+        dynarray_replace_at(&label_layer->ids, undo_context->index, &undo_context->id);
+        dynarray_replace_at(&label_layer->positions, undo_context->index, &undo_context->position);
+        dynarray_replace_at(&label_layer->colors, undo_context->index, &undo_context->color);
+        dynarray_replace_at(&label_layer->texts, undo_context->index, &undo_context->text);
     } break;
 
-    case UNDO_SWAP: {
-        dynarray_swap(label_layer->ids, undo_context->index, undo_context->index2);
-        dynarray_swap(label_layer->positions, undo_context->index, undo_context->index2);
-        dynarray_swap(label_layer->colors, undo_context->index, undo_context->index2);
-        dynarray_swap(label_layer->texts, undo_context->index, undo_context->index2);
+    case LABEL_UNDO_SWAP: {
+        dynarray_swap(&label_layer->ids, undo_context->index, undo_context->index2);
+        dynarray_swap(&label_layer->positions, undo_context->index, undo_context->index2);
+        dynarray_swap(&label_layer->colors, undo_context->index, undo_context->index2);
+        dynarray_swap(&label_layer->texts, undo_context->index, undo_context->index2);
     } break;
     }
 }
 
-#define UNDO_PUSH(HISTORY, CONTEXT)                                     \
+#define LABEL_UNDO_PUSH(HISTORY, CONTEXT)                                     \
     do {                                                                \
-        UndoContext context = (CONTEXT);                                \
+        LabelUndoContext context = (CONTEXT);                                \
         undo_history_push(                                              \
             HISTORY,                                                    \
             label_layer_undo,                                           \
@@ -178,31 +179,10 @@ LabelLayer *create_label_layer(const char *id_name_prefix)
     }
     label_layer->lt = lt;
 
-    label_layer->ids = PUSH_LT(
-        lt,
-        create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
-        destroy_dynarray);
-    if (label_layer->ids == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-
-    label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Vec2f)), destroy_dynarray);
-    if (label_layer->positions == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-
-    label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
-    if (label_layer->colors == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-
-    label_layer->texts = PUSH_LT(
-        lt,
-        create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE),
-        destroy_dynarray);
-    if (label_layer->texts == NULL) {
-        RETURN_LT(lt, NULL);
-    }
+    label_layer->ids = create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE);
+    label_layer->positions = create_dynarray(sizeof(Vec2f));
+    label_layer->colors = create_dynarray(sizeof(Color));
+    label_layer->texts = create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE);
 
     label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
     label_layer->selection = -1;
@@ -262,9 +242,9 @@ LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const c
 
         Color color = hexstr(hex);
 
-        dynarray_push(label_layer->ids, id);
-        dynarray_push(label_layer->positions, &position);
-        dynarray_push(label_layer->colors, &color);
+        dynarray_push(&label_layer->ids, id);
+        dynarray_push(&label_layer->positions, &position);
+        dynarray_push(&label_layer->colors, &color);
 
         line = line_stream_next(line_stream);
         if (line == NULL) {
@@ -274,7 +254,7 @@ LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const c
         char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0};
         memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1);
         trim_endline(label_text);
-        dynarray_push(label_layer->texts, &label_text);
+        dynarray_push(&label_layer->texts, &label_text);
     }
 
     return label_layer;
@@ -283,9 +263,40 @@ LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const c
 void destroy_label_layer(LabelLayer *label_layer)
 {
     trace_assert(label_layer);
+
+    free(label_layer->ids.data);
+    free(label_layer->positions.data);
+    free(label_layer->colors.data);
+    free(label_layer->texts.data);
+
     destroy_lt(label_layer->lt);
 }
 
+static inline
+Rect boundary_of_element(const LabelLayer *label_layer,
+                         size_t i,
+                         Vec2f position)
+{
+    trace_assert(i < label_layer->texts.count);
+
+    char *ids = (char *)label_layer->ids.data;
+    char *texts = (char *)label_layer->texts.data;
+
+    return rect_boundary2(
+        sprite_font_boundary_box(
+            position,
+            LABELS_SIZE,
+            texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
+        sprite_font_boundary_box(
+            vec_sum(
+                position,
+                vec_mult(
+                    vec(0.0f, FONT_CHAR_HEIGHT),
+                    LABELS_SIZE)),
+            vec(1.0f, 1.0f),
+            ids + i * LABEL_LAYER_ID_MAX_SIZE));
+}
+
 int label_layer_render(const LabelLayer *label_layer,
                        const Camera *camera,
                        int active)
@@ -297,11 +308,11 @@ int label_layer_render(const LabelLayer *label_layer,
         return -1;
     }
 
-    size_t n = dynarray_count(label_layer->ids);
-    char *ids = dynarray_data(label_layer->ids);
-    Vec2f *positions = dynarray_data(label_layer->positions);
-    Color *colors = dynarray_data(label_layer->colors);
-    char *texts = dynarray_data(label_layer->texts);
+    size_t n = label_layer->ids.count;
+    char *ids = (char *)label_layer->ids.data;
+    Vec2f *positions = (Vec2f *)label_layer->positions.data;
+    Color *colors = (Color *)label_layer->colors.data;
+    char *texts = (char *)label_layer->texts.data;
 
     /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
     for (size_t i = 0; i < n; ++i) {
@@ -340,9 +351,11 @@ int label_layer_render(const LabelLayer *label_layer,
             if (edit_field_render_world(
                     label_layer->edit_field,
                     camera,
-                    vec_sub(
+                    vec_sum(
                         position,
-                        vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
+                        vec_mult(
+                            vec(0.0f, FONT_CHAR_HEIGHT),
+                            LABELS_SIZE))) < 0) {
                 return -1;
             }
         } else {
@@ -353,30 +366,26 @@ int label_layer_render(const LabelLayer *label_layer,
                     color_scale(
                         color_invert(color),
                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
-                    vec_sub(position, vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
+                    vec_sum(
+                        position,
+                        vec_mult(
+                            vec(0.0f, FONT_CHAR_HEIGHT),
+                            LABELS_SIZE))) < 0) {
                 return -1;
             }
         }
 
         // Label Selection
+        // TODO(#1160): Label Selection has to be internal (just like in Rect Layer)
         if (active && label_layer->selection == (int) i) {
             Rect selection =
                 rect_pad(
                     camera_rect(
                         camera,
-                        rect_boundary2(
-                            sprite_font_boundary_box(
-                                camera_font(camera),
-                                position,
-                                LABELS_SIZE,
-                                texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE),
-                            sprite_font_boundary_box(
-                                camera_font(camera),
-                                vec_sub(
-                                    position,
-                                    vec(0.0f, FONT_CHAR_HEIGHT)),
-                                vec(1.0f, 1.0f),
-                                ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE))),
+                        boundary_of_element(
+                            label_layer,
+                            i,
+                            position)),
                     LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
 
 
@@ -396,32 +405,20 @@ int label_layer_render(const LabelLayer *label_layer,
 
 static
 int label_layer_element_at(LabelLayer *label_layer,
-                           const Sprite_font *font,
                            Vec2f position)
 {
     trace_assert(label_layer);
 
-    const int n = (int) dynarray_count(label_layer->texts);
-    char *ids = dynarray_data(label_layer->ids);
-    char *texts = dynarray_data(label_layer->texts);
-    Vec2f *positions = dynarray_data(label_layer->positions);
+    Vec2f *positions = (Vec2f*)label_layer->positions.data;
 
+    const int n = (int) label_layer->texts.count;
     for (int i = n - 1; i >= 0; --i) {
-        Rect boundary = rect_boundary2(
-            sprite_font_boundary_box(
-                font,
-                positions[i],
-                LABELS_SIZE,
-                texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
-            sprite_font_boundary_box(
-                font,
-                vec_sub(
-                    positions[i],
-                    vec(0.0f, FONT_CHAR_HEIGHT)),
-                vec(1.0f, 1.0f),
-                ids + i * LABEL_LAYER_ID_MAX_SIZE));
-
-        if (rect_contains_point(boundary, position)) {
+        if (rect_contains_point(
+                boundary_of_element(
+                    label_layer,
+                    (size_t) i,
+                    positions[i]),
+                position)) {
             return i;
         }
     }
@@ -436,12 +433,12 @@ void label_layer_delete_selected_label(LabelLayer *label_layer,
     trace_assert(label_layer);
     trace_assert(label_layer->selection >= 0);
 
-    UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_DELETE));
+    LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE));
 
-    dynarray_delete_at(label_layer->ids, (size_t)label_layer->selection);
-    dynarray_delete_at(label_layer->positions, (size_t)label_layer->selection);
-    dynarray_delete_at(label_layer->colors, (size_t)label_layer->selection);
-    dynarray_delete_at(label_layer->texts, (size_t)label_layer->selection);
+    dynarray_delete_at(&label_layer->ids, (size_t)label_layer->selection);
+    dynarray_delete_at(&label_layer->positions, (size_t)label_layer->selection);
+    dynarray_delete_at(&label_layer->colors, (size_t)label_layer->selection);
+    dynarray_delete_at(&label_layer->texts, (size_t)label_layer->selection);
 
     label_layer->selection = -1;
 }
@@ -461,18 +458,18 @@ int label_layer_add_label(LabelLayer *label_layer,
              label_layer->id_name_prefix,
              label_layer->id_name_counter++);
 
-    size_t n = dynarray_count(label_layer->ids);
+    size_t n = label_layer->ids.count;
 
-    dynarray_push(label_layer->ids, id);
-    dynarray_push(label_layer->positions, &position);
-    dynarray_push(label_layer->colors, &color);
-    dynarray_push_empty(label_layer->texts);
+    dynarray_push(&label_layer->ids, id);
+    dynarray_push(&label_layer->positions, &position);
+    dynarray_push(&label_layer->colors, &color);
+    dynarray_push_empty(&label_layer->texts);
     memcpy(
-        dynarray_pointer_at(label_layer->texts, n),
+        dynarray_pointer_at(&label_layer->texts, n),
         text,
         min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
 
-    UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_ADD));
+    LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD));
 
     return (int) n;
 }
@@ -484,15 +481,15 @@ void label_layer_swap_elements(LabelLayer *label_layer,
 {
     trace_assert(label_layer);
     trace_assert(undo_history);
-    trace_assert(a < dynarray_count(label_layer->positions));
-    trace_assert(b < dynarray_count(label_layer->positions));
+    trace_assert(a < label_layer->positions.count);
+    trace_assert(b < label_layer->positions.count);
 
-    dynarray_swap(label_layer->ids, a, b);
-    dynarray_swap(label_layer->positions, a, b);
-    dynarray_swap(label_layer->colors, a, b);
-    dynarray_swap(label_layer->texts, a, b);
+    dynarray_swap(&label_layer->ids, a, b);
+    dynarray_swap(&label_layer->positions, a, b);
+    dynarray_swap(&label_layer->colors, a, b);
+    dynarray_swap(&label_layer->texts, a, b);
 
-    UNDO_PUSH(undo_history, create_undo_swap_context(label_layer, a, b));
+    LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b));
 }
 
 static
@@ -523,10 +520,10 @@ int label_layer_idle_event(LabelLayer *label_layer,
         return 0;
     }
 
-    Color *colors = dynarray_data(label_layer->colors);
-    Vec2f *positions = dynarray_data(label_layer->positions);
-    char *ids = dynarray_data(label_layer->ids);
-    char *texts = dynarray_data(label_layer->texts);
+    Color *colors = (Color*)label_layer->colors.data;
+    Vec2f *positions = (Vec2f*)label_layer->positions.data;
+    char *ids = (char*)label_layer->ids.data;
+    char *texts = (char*)label_layer->texts.data;
 
     switch (event->type) {
     case SDL_MOUSEBUTTONDOWN: {
@@ -539,7 +536,6 @@ int label_layer_idle_event(LabelLayer *label_layer,
 
             const int element = label_layer_element_at(
                 label_layer,
-                camera_font(camera),
                 position);
 
             if (element >= 0) {
@@ -577,7 +573,7 @@ int label_layer_idle_event(LabelLayer *label_layer,
         case SDLK_UP: {
             if ((event->key.keysym.mod & KMOD_SHIFT)
                 && (label_layer->selection >= 0)
-                && ((size_t)(label_layer->selection + 1) < dynarray_count(label_layer->positions))) {
+                && ((size_t)(label_layer->selection + 1) < label_layer->positions.count)) {
                 label_layer_swap_elements(
                     label_layer,
                     (size_t) label_layer->selection,
@@ -590,7 +586,7 @@ int label_layer_idle_event(LabelLayer *label_layer,
         case SDLK_DOWN: {
             if ((event->key.keysym.mod & KMOD_SHIFT)
                 && (label_layer->selection > 0)
-                && ((size_t) label_layer->selection < dynarray_count(label_layer->positions))) {
+                && ((size_t) label_layer->selection < label_layer->positions.count)) {
                 label_layer_swap_elements(
                     label_layer,
                     (size_t) label_layer->selection,
@@ -639,14 +635,14 @@ int label_layer_idle_event(LabelLayer *label_layer,
 
         case SDLK_c: {
             if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
-                clipboard = 1;
-                dynarray_copy_to(label_layer->texts, clipboard_text, (size_t)label_layer->selection);
-                dynarray_copy_to(label_layer->colors, &clipboard_color, (size_t)label_layer->selection);
+                label_clipboard = 1;
+                dynarray_copy_to(&label_layer->texts, label_clipboard_text, (size_t)label_layer->selection);
+                dynarray_copy_to(&label_layer->colors, &label_clipboard_color, (size_t)label_layer->selection);
             }
         } break;
 
         case SDLK_v: {
-            if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
+            if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) {
                 int x, y;
                 SDL_GetMouseState(&x, &y);
                 Vec2f position = camera_map_screen(camera, x, y);
@@ -654,8 +650,8 @@ int label_layer_idle_event(LabelLayer *label_layer,
                 label_layer_add_label(
                     label_layer,
                     position,
-                    clipboard_color,
-                    clipboard_text,
+                    label_clipboard_color,
+                    label_clipboard_text,
                     undo_history);
             }
         } break;
@@ -666,6 +662,38 @@ int label_layer_idle_event(LabelLayer *label_layer,
     return 0;
 }
 
+static
+void snap_inter_position(LabelLayer *label_layer, float snap_threshold)
+{
+    trace_assert(label_layer);
+    trace_assert(label_layer->selection >= 0);
+    trace_assert(label_layer->state == LABEL_LAYER_MOVE);
+
+    const size_t n = label_layer->positions.count;
+    Vec2f *positions = (Vec2f*)label_layer->positions.data;
+
+    Rect a = boundary_of_element(
+        label_layer,
+        (size_t) label_layer->selection,
+        label_layer->inter_position);
+
+    for (size_t i = 0; i < n; ++i) {
+        if (i == (size_t) label_layer->selection) continue;
+
+        const Rect b = boundary_of_element(label_layer, i, positions[i]);
+
+        if (segment_overlap(vec(a.x, a.x + a.w), vec(b.x,  b.x + b.w))) {
+            snap_seg2seg(&label_layer->inter_position.y,
+                         b.y, a.h, b.h, snap_threshold);
+        }
+
+        if (segment_overlap(vec(a.y, a.y + a.h), vec(b.y,  b.y  + b.h))) {
+            snap_seg2seg(&label_layer->inter_position.x,
+                         b.x, a.w, b.w, snap_threshold);
+        }
+    }
+}
+
 static
 int label_layer_move_event(LabelLayer *label_layer,
                            const SDL_Event *event,
@@ -677,7 +705,7 @@ int label_layer_move_event(LabelLayer *label_layer,
     trace_assert(camera);
     trace_assert(label_layer->selection >= 0);
 
-    Vec2f *positions = dynarray_data(label_layer->positions);
+    Vec2f *positions = (Vec2f*)label_layer->positions.data;
 
     switch (event->type) {
     case SDL_MOUSEMOTION: {
@@ -703,6 +731,8 @@ int label_layer_move_event(LabelLayer *label_layer,
                 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
             }
         }
+
+        snap_inter_position(label_layer, SNAPPING_THRESHOLD);
     } break;
 
     case SDL_MOUSEBUTTONUP: {
@@ -713,10 +743,10 @@ int label_layer_move_event(LabelLayer *label_layer,
                         positions[label_layer->selection]));
 
             if (distance > 1e-6) {
-                UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
+                LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
 
                 dynarray_replace_at(
-                    label_layer->positions,
+                    &label_layer->positions,
                     (size_t)label_layer->selection,
                     &label_layer->inter_position);
             }
@@ -745,10 +775,10 @@ int label_layer_edit_text_event(LabelLayer *label_layer,
     case SDL_KEYDOWN: {
         switch (event->key.keysym.sym) {
         case SDLK_RETURN: {
-            UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
+            LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
 
             char *text =
-                (char*)dynarray_data(label_layer->texts) + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
+                (char*)label_layer->texts.data + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
             memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
             memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
             label_layer->state = LABEL_LAYER_IDLE;
@@ -784,10 +814,10 @@ int label_layer_edit_id_event(LabelLayer *label_layer,
     case SDL_KEYDOWN: {
         switch (event->key.keysym.sym) {
         case SDLK_RETURN: {
-            UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
+            LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
 
             char *id =
-                (char*)dynarray_data(label_layer->ids) + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
+                (char*)label_layer->ids.data + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
             memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
             memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
             label_layer->state = LABEL_LAYER_IDLE;
@@ -834,10 +864,10 @@ int label_layer_recolor_event(LabelLayer *label_layer,
             color_picker_rgba(&label_layer->color_picker);
 
         if (!color_picker_drag(&label_layer->color_picker)) {
-            UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
+            LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
 
             dynarray_replace_at(
-                label_layer->colors,
+                &label_layer->colors,
                 (size_t) label_layer->selection,
                 &label_layer->inter_color);
             label_layer->state = LABEL_LAYER_IDLE;
@@ -879,27 +909,27 @@ int label_layer_event(LabelLayer *label_layer,
 
 size_t label_layer_count(const LabelLayer *label_layer)
 {
-    return dynarray_count(label_layer->ids);
+    return label_layer->ids.count;
 }
 
 char *label_layer_ids(const LabelLayer *label_layer)
 {
-    return dynarray_data(label_layer->ids);
+    return (char *)label_layer->ids.data;
 }
 
 Vec2f *label_layer_positions(const LabelLayer *label_layer)
 {
-    return dynarray_data(label_layer->positions);
+    return (Vec2f *)label_layer->positions.data;
 }
 
 Color *label_layer_colors(const LabelLayer *label_layer)
 {
-    return dynarray_data(label_layer->colors);
+    return (Color *)label_layer->colors.data;
 }
 
 char *labels_layer_texts(const LabelLayer *label_layer)
 {
-    return dynarray_data(label_layer->texts);
+    return (char *)label_layer->texts.data;
 }
 
 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
@@ -907,11 +937,11 @@ int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
     trace_assert(label_layer);
     trace_assert(filedump);
 
-    size_t n = dynarray_count(label_layer->ids);
-    char *ids = dynarray_data(label_layer->ids);
-    Vec2f *positions = dynarray_data(label_layer->positions);
-    Color *colors = dynarray_data(label_layer->colors);
-    char *texts = dynarray_data(label_layer->texts);
+    size_t n = label_layer->ids.count;
+    char *ids = (char *)label_layer->ids.data;
+    Vec2f *positions = (Vec2f *)label_layer->positions.data;
+    Color *colors = (Color *)label_layer->colors.data;
+    char *texts = (char *)label_layer->texts.data;
 
     fprintf(filedump, "%zd\n", n);
     for (size_t i = 0; i < n; ++i) {