]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/level_editor/rect_layer.c
Add TODO(#943)
[nothing.git] / src / game / level / level_editor / rect_layer.c
index 659028d4a98840b46820ed4b8dcc6a272f030fea..69c410a45f4647780d606da5b762a35eef804a62 100644 (file)
@@ -8,17 +8,95 @@
 #include "rect_layer.h"
 #include "dynarray.h"
 #include "system/line_stream.h"
-#include "proto_rect.h"
 #include "color_picker.h"
+#include "system/str.h"
 
+#define RECT_LAYER_ID_MAX_SIZE 36
+#define RECT_LAYER_SELECTION_THICCNESS 5.0f
+#define PROTO_AREA_THRESHOLD 10.0
+
+// TODO(#942): RectLayer does not allow to move rectangle arround
+typedef enum {
+    RECT_LAYER_IDLE = 0,
+    RECT_LAYER_PROTO,
+    RECT_LAYER_RESIZE
+} RectLayerState;
+
+/* TODO(#886): RectLayer does not allow to modify ids of Rects */
 struct RectLayer {
     Lt *lt;
+    RectLayerState state;
+    Dynarray *ids;
     Dynarray *rects;
     Dynarray *colors;
-    ProtoRect proto_rect;
     ColorPicker color_picker;
+    Vec proto_begin;
+    Vec proto_end;
+    int selection;
 };
 
+static int rect_layer_rect_at(RectLayer *layer, Vec position)
+{
+    trace_assert(layer);
+
+    const size_t n = dynarray_count(layer->rects);
+    Rect *rects = dynarray_data(layer->rects);
+
+    for (size_t i = 0; i < n; ++i) {
+        if (rect_contains_point(rects[i], position)) {
+            return (int) i;
+        }
+    }
+
+    return -1;
+}
+
+static int rect_layer_delete_rect_at(RectLayer *layer, size_t i)
+{
+    trace_assert(layer);
+
+    dynarray_delete_at(layer->rects, i);
+    dynarray_delete_at(layer->colors, i);
+    dynarray_delete_at(layer->ids, i);
+
+    return 0;
+}
+
+static Rect rect_layer_resize_anchor(const RectLayer *layer, size_t i)
+{
+    Rect *rects = dynarray_data(layer->rects);
+    return rect(rects[i].x + rects[i].w,
+                rects[i].y + rects[i].h,
+                RECT_LAYER_SELECTION_THICCNESS * 2.0f,
+                RECT_LAYER_SELECTION_THICCNESS * 2.0f);
+}
+
+static int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color)
+{
+    trace_assert(layer);
+
+    if (dynarray_push(layer->rects, &rect) < 0) {
+        return -1;
+    }
+
+    if (dynarray_push(layer->colors, &color) < 0) {
+        return -1;
+    }
+
+    char id[RECT_LAYER_ID_MAX_SIZE];
+    for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
+        id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
+    }
+    id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
+
+    if (dynarray_push(layer->ids, id)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+
 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
 {
     LayerPtr layer = {
@@ -38,6 +116,14 @@ RectLayer *create_rect_layer(void)
     }
     layer->lt = lt;
 
+    layer->ids = PUSH_LT(
+        lt,
+        create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
+        destroy_dynarray);
+    if (layer->ids == NULL) {
+        RETURN_LT(lt, NULL);
+    }
+
     layer->rects = PUSH_LT(
         lt,
         create_dynarray(sizeof(Rect)),
@@ -54,9 +140,8 @@ RectLayer *create_rect_layer(void)
         RETURN_LT(lt, NULL);
     }
 
-    layer->color_picker.color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
-    layer->proto_rect.color_current = &layer->color_picker.color;
-    layer->proto_rect.layer_current = layer;
+    layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
+    layer->selection = -1;
 
     return layer;
 }
@@ -76,7 +161,7 @@ RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
     }
 
     size_t count = 0;
-    if (sscanf(line, "%lu", &count) < 0) {
+    if (sscanf(line, "%zu", &count) < 0) {
         RETURN_LT(layer->lt, NULL);
     }
 
@@ -88,17 +173,22 @@ RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
 
         char hex[7];
         Rect rect;
+        char id[RECT_LAYER_ID_MAX_SIZE];
 
-        if (sscanf(line, "%f%f%f%f%6s\n",
+        if (sscanf(line,
+                   "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
+                   id,
                    &rect.x, &rect.y,
                    &rect.w, &rect.h,
                    hex) < 0) {
             RETURN_LT(layer->lt, NULL);
         }
 
-        if (rect_layer_add_rect(layer, rect, hexstr(hex)) < 0) {
-            RETURN_LT(layer->lt, NULL);
-        }
+        Color color = hexstr(hex);
+
+        dynarray_push(layer->rects, &rect);
+        dynarray_push(layer->ids, id);
+        dynarray_push(layer->colors, &color);
     }
 
     return layer;
@@ -110,7 +200,7 @@ void destroy_rect_layer(RectLayer *layer)
     RETURN_LT0(layer->lt);
 }
 
-int rect_layer_render(const RectLayer *layer, Camera *camera, float fa)
+int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
 {
     trace_assert(layer);
     trace_assert(camera);
@@ -120,21 +210,46 @@ int rect_layer_render(const RectLayer *layer, Camera *camera, float fa)
     Color *colors = dynarray_data(layer->colors);
 
     for (size_t i = 0; i < n; ++i) {
+        if (layer->selection == (int) i) {
+            if (active) {
+                const Color color = color_invert(colors[i]);
+
+                if (camera_fill_rect(
+                        camera,
+                        // TODO(#943): thiccness of RectLayer selection should be probably based on zoom
+                        rect_scale(rects[i], RECT_LAYER_SELECTION_THICCNESS),
+                        color) < 0) {
+                    return -1;
+                }
+
+                if (camera_fill_rect(
+                        camera,
+                        rect_layer_resize_anchor(layer, i),
+                        color) < 0) {
+                    return -1;
+                }
+            }
+        }
+
         if (camera_fill_rect(
                 camera,
                 rects[i],
                 color_scale(
                     colors[i],
-                    rgba(1.0f, 1.0f, 1.0f, fa))) < 0) {
+                    rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
             return -1;
         }
+
     }
 
-    if (proto_rect_render(&layer->proto_rect, camera) < 0) {
-        return -1;
+    const Color color = color_picker_rgba(&layer->color_picker);
+    if (layer->state == RECT_LAYER_PROTO) {
+        if (camera_fill_rect(camera, rect_from_points(layer->proto_begin, layer->proto_end), color) < 0) {
+            return -1;
+        }
     }
 
-    if (color_picker_render(&layer->color_picker, camera) < 0) {
+    if (active && color_picker_render(&layer->color_picker, camera) < 0) {
         return -1;
     }
 
@@ -146,66 +261,108 @@ int rect_layer_event(RectLayer *layer, const SDL_Event *event, const Camera *cam
     trace_assert(layer);
     trace_assert(event);
 
-    switch(event->type) {
-    case SDL_MOUSEBUTTONDOWN:
-    case SDL_MOUSEBUTTONUP: {
-        bool selected = false;
-        if (color_picker_mouse_button(
-                &layer->color_picker,
-                &event->button,
-                &selected) < 0) {
-            return -1;
-        }
-
-        if (!selected && proto_rect_mouse_button(
-                &layer->proto_rect,
-                &event->button,
-                camera) < 0) {
-            return -1;
-        }
-
-    } break;
-
-    case SDL_MOUSEMOTION: {
-        if (proto_rect_mouse_motion(&layer->proto_rect, &event->motion, camera) < 0) {
-            return -1;
-        }
-    } break;
-    }
-
-    return 0;
-}
-
-int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color)
-{
-    trace_assert(layer);
-
-    if (dynarray_push(layer->rects, &rect) < 0) {
+    int selected = 0;
+    if (color_picker_event(&layer->color_picker, event, &selected) < 0) {
         return -1;
     }
 
-    if (dynarray_push(layer->colors, &color) < 0) {
-        return -1;
+    if (selected) {
+        return 0;
     }
 
-    return 0;
-}
-
-int rect_layer_delete_rect_at(RectLayer *layer, Vec position)
-{
-    trace_assert(layer);
-
-    const size_t n = dynarray_count(layer->rects);
-    Rect *rects = dynarray_data(layer->rects);
-
-    for (size_t i = 0; i < n; ++i) {
-        if (rect_contains_point(rects[i], position)) {
-            dynarray_delete_at(layer->rects, i);
-            dynarray_delete_at(layer->colors, i);
-            return 0;
+    const Color color = color_picker_rgba(&layer->color_picker);
+    if (layer->state == RECT_LAYER_PROTO) {
+        switch (event->type) {
+        case SDL_MOUSEBUTTONUP: {
+            switch (event->button.button) {
+            case SDL_BUTTON_LEFT: {
+                const Rect real_rect =
+                    rect_from_points(
+                        layer->proto_begin,
+                        layer->proto_end);
+                const float area = real_rect.w * real_rect.h;
+
+                if (area >= PROTO_AREA_THRESHOLD) {
+                    rect_layer_add_rect(layer, real_rect, color);
+                } else {
+                    log_info("The area is too small %f. Such small box won't be created.\n", area);
+                }
+                layer->state = RECT_LAYER_IDLE;
+            } break;
+            }
+        } break;
+
+        case SDL_MOUSEMOTION: {
+            layer->proto_end = camera_map_screen(
+                camera,
+                event->motion.x,
+                event->motion.y);
+        } break;
+        }
+    } else if (layer->state == RECT_LAYER_RESIZE) {
+        switch (event->type) {
+        case SDL_MOUSEMOTION: {
+            Rect *rects = dynarray_data(layer->rects);
+            trace_assert(layer->selection >= 0);
+            rects[layer->selection] = rect_from_points(
+                vec(rects[layer->selection].x, rects[layer->selection].y),
+                vec_sum(
+                    camera_map_screen(
+                        camera,
+                        event->button.x,
+                        event->button.y),
+                    vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
+                        RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
+        } break;
+
+        case SDL_MOUSEBUTTONUP: {
+            layer->state = RECT_LAYER_IDLE;
+        } break;
+        }
+    } else {
+        switch (event->type) {
+        case SDL_MOUSEBUTTONDOWN: {
+            switch (event->button.button) {
+            case SDL_BUTTON_LEFT: {
+                Point position = camera_map_screen(
+                    camera,
+                    event->button.x,
+                    event->button.y);
+
+                if (layer->selection >= 0 &&
+                    rect_contains_point(
+                        rect_layer_resize_anchor(
+                            layer,
+                            (size_t)layer->selection),
+                        position)) {
+                    layer->state = RECT_LAYER_RESIZE;
+                } else {
+                    layer->selection = rect_layer_rect_at(layer, position);
+
+                    if (layer->selection < 0) {
+                        layer->state = RECT_LAYER_PROTO;
+                        layer->proto_begin = position;
+                        layer->proto_end = position;
+                    }
+                }
+            } break;
+            }
+        } break;
+
+        case SDL_KEYDOWN: {
+            switch (event->key.keysym.sym) {
+            case SDLK_DELETE: {
+                if (layer->selection >= 0) {
+                    rect_layer_delete_rect_at(layer, (size_t) layer->selection);
+                    layer->selection = -1;
+                }
+            } break;
+            }
+        } break;
         }
     }
 
+
     return 0;
 }
 
@@ -223,3 +380,30 @@ const Color *rect_layer_colors(const RectLayer *layer)
 {
     return dynarray_data(layer->colors);
 }
+
+const char *rect_layer_ids(const RectLayer *layer)
+{
+    return dynarray_data(layer->ids);
+}
+
+int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
+{
+    trace_assert(layer);
+    trace_assert(filedump);
+
+    size_t n = dynarray_count(layer->ids);
+    char *ids = dynarray_data(layer->ids);
+    Rect *rects = dynarray_data(layer->rects);
+    Color *colors = dynarray_data(layer->colors);
+
+    fprintf(filedump, "%zd\n", n);
+    for (size_t i = 0; i < n; ++i) {
+        fprintf(filedump, "%s %f %f %f %f ",
+                ids + RECT_LAYER_ID_MAX_SIZE * i,
+                rects[i].x, rects[i].y, rects[i].w, rects[i].h);
+        color_hex_to_stream(colors[i], filedump);
+        fprintf(filedump, "\n");
+    }
+
+    return 0;
+}