]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/level_editor/rect_layer.c
(#1253) Let subtract tool cut out a window inside of a platform
[nothing.git] / src / game / level / level_editor / rect_layer.c
index 1bbbd8fab1dae6c067e8366e68e508aa6bea4757..5cee90efaaf635f8263f3d5f08beb4542df65503 100644 (file)
@@ -2,7 +2,6 @@
 #include <errno.h>
 
 #include "game/camera.h"
-#include "system/lt.h"
 #include "system/stacktrace.h"
 #include "system/nth_alloc.h"
 #include "system/log.h"
 #include "color.h"
 #include "rect_layer.h"
 #include "dynarray.h"
-#include "system/line_stream.h"
-#include "color_picker.h"
 #include "system/str.h"
-#include "ui/edit_field.h"
 #include "undo_history.h"
 #include "game/level/action.h"
-#include "action_picker.h"
 #include "game.h"
+#include "math/extrema.h"
 
 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
 #define CREATE_AREA_THRESHOLD 10.0
-#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]
@@ -46,53 +40,15 @@ static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
 };
 
 typedef enum {
-    RECT_LAYER_IDLE = 0,
-    RECT_LAYER_CREATE,
-    RECT_LAYER_RESIZE,
-    RECT_LAYER_MOVE,
-    RECT_LAYER_ID_RENAME,
-    RECT_LAYER_RECOLOR
-} RectLayerState;
-
-struct RectLayer {
-    Lt *lt;
-    RectLayerState state;
-    int resize_mask;
-    Dynarray *ids;
-    Dynarray *rects;
-    Dynarray *colors;
-    Dynarray *actions;
-    ColorPicker color_picker;
-    ActionPicker action_picker;
-    Vec2f create_begin;
-    Vec2f create_end;
-    int selection;
-    Vec2f move_anchor;          // The mouse offset from the left-top
-                                // corner of the rect during moving it
-    Edit_field *id_edit_field;
-    Color inter_color;
-    Rect inter_rect;
-    int id_name_counter;
-    const char *id_name_prefix;
-    Grid *grid;
-    Cursor *cursor;
-
-    // this is the initial size of the selected rectangle during a resize.
-    // we update this whenever the rectangle is resized, so we know the ratio
-    // to fix it to.
-    Vec2f initial_rectangle_size;
-};
-
-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;
@@ -103,77 +59,76 @@ 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));
+    trace_assert(index < layer->rects.count);
 
-    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, size_t index)
 {
     trace_assert(layer);
-    size_t index = (size_t) layer->selection;
-    trace_assert(index < dynarray_count(layer->rects));
+    trace_assert(index < layer->rects.count);
 
-    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);
-    dynarray_copy_to(layer->colors, &undo_context.element.color, index);
-    dynarray_copy_to(layer->ids, undo_context.element.id, index);
-    dynarray_copy_to(layer->actions, &undo_context.element.action, index);
+    dynarray_copy_to(&layer->rects, &undo_context.element.rect, index);
+    dynarray_copy_to(&layer->colors, &undo_context.element.color, index);
+    dynarray_copy_to(&layer->ids, undo_context.element.id, index);
+    dynarray_copy_to(&layer->actions, &undo_context.element.action, index);
     return undo_context;
 }
 
 static
-UndoContext create_undo_update_context(RectLayer *rect_layer)
+RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer, size_t index)
 {
-    UndoContext undo_context = create_undo_element_context(rect_layer);
-    undo_context.type = UNDO_UPDATE;
+    RectUndoContext undo_context = create_rect_undo_element_context(rect_layer, index);
+    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, size_t i)
 {
-    UndoContext undo_context = create_undo_element_context(rect_layer);
-    undo_context.type = UNDO_DELETE;
+    RectUndoContext undo_context = create_rect_undo_element_context(rect_layer, i);
+    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;
@@ -184,50 +139,50 @@ 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);
-        dynarray_delete_at(layer->ids, undo_context->add.index);
-        dynarray_delete_at(layer->actions, undo_context->add.index);
+        dynarray_delete_at(&layer->rects, undo_context->add.index);
+        dynarray_delete_at(&layer->colors, undo_context->add.index);
+        dynarray_delete_at(&layer->ids, undo_context->add.index);
+        dynarray_delete_at(&layer->actions, undo_context->add.index);
         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);
-        dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
-        dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
+        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);
+        dynarray_insert_before(&layer->ids, undo_context->element.index, &undo_context->element.id);
+        dynarray_insert_before(&layer->actions, undo_context->element.index, &undo_context->element.action);
         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);
-        dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
-        dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
+        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);
+        dynarray_replace_at(&layer->ids, undo_context->element.index, &undo_context->element.id);
+        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);
-        dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
-        dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
+        dynarray_swap(&layer->rects, undo_context->swap.index1, undo_context->swap.index2);
+        dynarray_swap(&layer->colors, undo_context->swap.index1, undo_context->swap.index2);
+        dynarray_swap(&layer->ids, undo_context->swap.index1, undo_context->swap.index2);
+        dynarray_swap(&layer->actions, undo_context->swap.index1, undo_context->swap.index2);
     } break;
     }
 }
 
-#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,                                            \
@@ -242,29 +197,22 @@ static int rect_layer_add_rect(RectLayer *layer,
 {
     trace_assert(layer);
 
-    if (dynarray_push(layer->rects, &rect) < 0) {
-        return -1;
-    }
-
-    if (dynarray_push(layer->colors, &color) < 0) {
-        return -1;
-    }
+    dynarray_push(&layer->rects, &rect);
+    dynarray_push(&layer->colors, &color);
 
     char id[ENTITY_MAX_ID_SIZE];
     snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
              layer->id_name_prefix,
              layer->id_name_counter++);
-    if (dynarray_push(layer->ids, id)) {
-        return -1;
-    }
+    dynarray_push(&layer->ids, id);
 
-    dynarray_push_empty(layer->actions);
+    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));
+            layer->rects.count - 1));
 
     return 0;
 }
@@ -273,8 +221,8 @@ static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
 {
     trace_assert(layer);
 
-    int n = (int) dynarray_count(layer->rects);
-    Rect *rects = dynarray_data(layer->rects);
+    int n = (int) layer->rects.count;
+    Rect *rects = (Rect*)layer->rects.data;
 
     for (int i = n - 1; i >= 0; --i) {
         if (rect_contains_point(rects[i], position)) {
@@ -289,29 +237,29 @@ static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
                                      UndoHistory *undo_history)
 {
     trace_assert(layer);
-    trace_assert(a < dynarray_count(layer->rects));
-    trace_assert(b < dynarray_count(layer->rects));
+    trace_assert(a < layer->rects.count);
+    trace_assert(b < layer->rects.count);
 
-    dynarray_swap(layer->rects, a, b);
-    dynarray_swap(layer->colors, a, b);
-    dynarray_swap(layer->ids, a, b);
-    dynarray_swap(layer->actions, a, b);
+    dynarray_swap(&layer->rects, a, b);
+    dynarray_swap(&layer->colors, a, 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,
+static int rect_layer_delete_rect_at_index(RectLayer *layer,
                                      size_t i,
                                      UndoHistory *undo_history)
 {
     trace_assert(layer);
 
-    UNDO_PUSH(undo_history, create_undo_delete_context(layer));
+    RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer, i));
 
-    dynarray_delete_at(layer->rects, i);
-    dynarray_delete_at(layer->colors, i);
-    dynarray_delete_at(layer->ids, i);
-    dynarray_delete_at(layer->actions, i);
+    dynarray_delete_at(&layer->rects, i);
+    dynarray_delete_at(&layer->colors, i);
+    dynarray_delete_at(&layer->ids, i);
+    dynarray_delete_at(&layer->actions, i);
 
     return 0;
 }
@@ -327,6 +275,34 @@ static int calc_resize_mask(Vec2f point, Rect rect)
     return mask;
 }
 
+#define TOOL_BUTTON_WIDTH 50.0f
+#define TOOL_BUTTON_HEIGHT 50.0f
+#define TOOL_BAR_PADDING 20.0f
+#define TOOL_BAR_BACKGROUND rgba(0.8f, 0.8f, 0.8f, 1.0f)
+#define TOOL_BAR_FOREGROUND rgba(0.2f, 0.2f, 0.2f, 1.0f)
+
+static
+Rect subtract_tool_button_rect(const Camera *camera)
+{
+    const Rect view_port = camera_view_port_screen(camera);
+    return rect(
+        TOOL_BAR_PADDING,
+        view_port.h - TOOL_BUTTON_HEIGHT - TOOL_BAR_PADDING,
+        TOOL_BUTTON_WIDTH,
+        TOOL_BUTTON_HEIGHT);
+}
+
+static
+Rect snapping_tool_button_rect(const Camera *camera)
+{
+    const Rect view_port = camera_view_port_screen(camera);
+    return rect(
+        TOOL_BAR_PADDING + TOOL_BUTTON_WIDTH + TOOL_BAR_PADDING,
+        view_port.h - TOOL_BUTTON_HEIGHT - TOOL_BAR_PADDING,
+        TOOL_BUTTON_WIDTH,
+        TOOL_BUTTON_HEIGHT);
+}
+
 static int rect_layer_event_idle(RectLayer *layer,
                                  const SDL_Event *event,
                                  const Camera *camera,
@@ -343,18 +319,31 @@ static int rect_layer_event_idle(RectLayer *layer,
 
     if (color_changed) {
         if (layer->selection >= 0) {
-            dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
+            dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
             layer->state = RECT_LAYER_RECOLOR;
         }
         return 0;
     }
 
-    Rect *rects = dynarray_data(layer->rects);
+    Rect *rects = (Rect*)layer->rects.data;
 
     switch (event->type) {
     case SDL_MOUSEBUTTONDOWN: {
         switch (event->button.button) {
         case SDL_BUTTON_LEFT: {
+            Vec2f screen_position =
+                vec((float) event->button.x,
+                    (float) event->button.y);
+            if (rect_contains_point(subtract_tool_button_rect(camera), screen_position)) {
+                layer->subtract_enabled = !layer->subtract_enabled;
+                return 0;
+            }
+
+            if (rect_contains_point(snapping_tool_button_rect(camera), screen_position)) {
+                layer->snapping_enabled = !layer->snapping_enabled;
+                return 0;
+            }
+
             Vec2f position = camera_map_screen(
                 camera,
                 event->button.x,
@@ -363,15 +352,19 @@ static int rect_layer_event_idle(RectLayer *layer,
                 rect_layer_rect_at(layer, position);
 
 
-            Color *colors = dynarray_data(layer->colors);
+            Color *colors = (Color*)layer->colors.data;
 
-            if (layer->selection >= 0 &&
-                layer->selection == rect_at_position &&
-                (layer->resize_mask = calc_resize_mask(
-                    vec((float) event->button.x, (float)event->button.y),
-                    camera_rect(camera, rects[layer->selection])))) {
+            if (layer->subtract_enabled) {
+                layer->state = RECT_LAYER_SUBTRACT;
+                layer->create_begin = position;
+                layer->create_end = position;
+            } else if (layer->selection >= 0 &&
+                       layer->selection == rect_at_position &&
+                       (layer->resize_mask = calc_resize_mask(
+                           vec((float) event->button.x, (float)event->button.y),
+                           camera_rect(camera, rects[layer->selection])))) {
                 layer->state = RECT_LAYER_RESIZE;
-                dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
+                dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
             } else if (rect_at_position >= 0) {
                 layer->selection = rect_at_position;
                 layer->state = RECT_LAYER_MOVE;
@@ -382,7 +375,7 @@ static int rect_layer_event_idle(RectLayer *layer,
                         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);
+                dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
             } else {
                 layer->selection = rect_at_position;
 
@@ -392,11 +385,6 @@ static int rect_layer_event_idle(RectLayer *layer,
                     layer->create_end = position;
                 }
             }
-
-            if (layer->selection >= 0) {
-                layer->initial_rectangle_size = vec(rects[layer->selection].w,
-                    rects[layer->selection].h);
-            }
         } break;
         }
     } break;
@@ -423,7 +411,7 @@ static int rect_layer_event_idle(RectLayer *layer,
         case SDLK_UP: {
             if ((event->key.keysym.mod & KMOD_SHIFT)
                 && (layer->selection >= 0)
-                && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
+                && ((size_t)(layer->selection + 1) < layer->rects.count)) {
                 rect_layer_swap_elements(
                     layer,
                     (size_t) layer->selection,
@@ -436,7 +424,7 @@ static int rect_layer_event_idle(RectLayer *layer,
         case SDLK_DOWN: {
             if ((event->key.keysym.mod & KMOD_SHIFT)
                 && (layer->selection > 0)
-                && ((size_t) layer->selection < dynarray_count(layer->rects))) {
+                && ((size_t) layer->selection < layer->rects.count)) {
                 rect_layer_swap_elements(
                     layer,
                     (size_t) layer->selection,
@@ -448,24 +436,32 @@ static int rect_layer_event_idle(RectLayer *layer,
 
         case SDLK_DELETE: {
             if (layer->selection >= 0) {
-                rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
+                rect_layer_delete_rect_at_index(
+                    layer,
+                    (size_t) layer->selection,
+                    undo_history);
                 layer->selection = -1;
             }
         } 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);
-                Color *colors = dynarray_data(layer->colors);
+                const char *ids = (char*)layer->ids.data;
+                Color *colors = (Color*)layer->colors.data;
 
                 edit_field_restyle(
-                    layer->id_edit_field,
+                    &layer->id_edit_field,
                     RECT_LAYER_ID_LABEL_SIZE,
                     color_invert(colors[layer->selection]));
 
                 layer->state = RECT_LAYER_ID_RENAME;
                 edit_field_replace(
-                    layer->id_edit_field,
+                    &layer->id_edit_field,
                     ids + layer->selection * ENTITY_MAX_ID_SIZE);
                 SDL_StartTextInput();
             }
@@ -473,14 +469,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);
@@ -488,8 +484,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;
@@ -500,6 +496,144 @@ static int rect_layer_event_idle(RectLayer *layer,
     return 0;
 }
 
+#define GEOMETRY_CAPACITY 256
+
+typedef struct {
+    size_t first;
+    size_t count;
+    Rect rects[GEOMETRY_CAPACITY];
+    Color colors[GEOMETRY_CAPACITY];
+} Geometry;
+
+static
+void push_geometry(Geometry *geometry, Rect rect, Color color)
+{
+    assert(geometry);
+    // TODO(#1252): push_geometry may fail if there is too many rects produced
+    assert(geometry->count < GEOMETRY_CAPACITY);
+
+    if ((rect.w * rect.h) > 1e-6f) {
+        size_t i = (geometry->first + geometry->count) % GEOMETRY_CAPACITY;
+        geometry->rects[i] = rect;
+        geometry->colors[i] = color;
+        geometry->count++;
+    }
+}
+
+static
+void subtract_rect_from_rect(Rect a, Color color_a, Rect c, Geometry *result)
+{
+    assert(result);
+
+    Rect b = rects_overlap_area(a, c);
+
+    if (b.w * b.h < 1e-6) {
+        push_geometry(result, a, color_a);
+        return;
+    }
+
+    push_geometry(result, (Rect) {a.x, a.y, a.w, b.y - a.y}, color_a);
+    push_geometry(result, (Rect) {a.x, b.y, b.x - a.x, b.h}, color_a);
+    push_geometry(result, (Rect) {
+        b.x + b.w,
+        b.y,
+        a.w - (b.x - a.x) - b.w,
+        b.h
+    }, color_a);
+    push_geometry(result, (Rect) {
+        a.x,
+        b.y + b.h,
+        a.w,
+        a.h - (b.y - a.y) - b.h
+    }, color_a);
+}
+
+static
+void subtract_rect_from_geometry(Geometry *result, Rect b)
+{
+    assert(result);
+
+    size_t count = result->count;
+    size_t first = result->first;
+
+    for (size_t i = 0; i < count; ++i) {
+        result->first = (result->first + 1) % GEOMETRY_CAPACITY;
+        result->count -= 1;
+        subtract_rect_from_rect(
+            result->rects[(i + first) % GEOMETRY_CAPACITY],
+            result->colors[(i + first) % GEOMETRY_CAPACITY],
+            b,
+            result);
+    }
+}
+
+static int rect_layer_event_subtract(RectLayer *layer,
+                                     const SDL_Event *event,
+                                     const Camera *camera,
+                                     UndoHistory *undo_history)
+{
+    trace_assert(layer);
+    trace_assert(event);
+    trace_assert(camera);
+    trace_assert(undo_history);
+
+    Rect *rects = layer->rects.data;
+    Color *colors = layer->colors.data;
+
+    switch (event->type) {
+    case SDL_MOUSEBUTTONUP: {
+        switch (event->button.button) {
+        case SDL_BUTTON_LEFT: {
+            const Rect real_rect =
+                rect_from_points(
+                    layer->create_begin,
+                    layer->create_end);
+            const float area = real_rect.w * real_rect.h;
+
+            Geometry geometry = {0};
+
+            if (area >= CREATE_AREA_THRESHOLD) {
+                for (size_t i = 0; i < layer->rects.count;) {
+                    Rect overlap_area = rects_overlap_area(
+                        real_rect,
+                        rects[i]);
+                    if (overlap_area.w * overlap_area.h > 1e-6) {
+                        push_geometry(&geometry, rects[i], colors[i]);
+                        rect_layer_delete_rect_at_index(layer, i, undo_history);
+                    } else {
+                        i++;
+                    }
+                }
+
+                subtract_rect_from_geometry(&geometry, real_rect);
+
+                for (size_t i = 0; i < geometry.count; ++i) {
+                    size_t j = (i + geometry.first) % GEOMETRY_CAPACITY;
+                    rect_layer_add_rect(
+                        layer,
+                        geometry.rects[j],
+                        geometry.colors[j],
+                        undo_history);
+                }
+            } else {
+                log_info("The area is too small %f. Such small box won't be cut.\n", area);
+            }
+            layer->state = RECT_LAYER_IDLE;
+        } break;
+        }
+    } break;
+
+    case SDL_MOUSEMOTION: {
+        layer->create_end = camera_map_screen(
+            camera,
+            event->motion.x,
+            event->motion.y);
+    } break;
+    }
+
+    return 0;
+}
+
 static int rect_layer_event_create(RectLayer *layer,
                                    const SDL_Event *event,
                                    const Camera *camera,
@@ -544,38 +678,29 @@ static int rect_layer_event_create(RectLayer *layer,
 }
 
 static
-void fix_rect_ratio(RectLayer *layer,
-                    float *x, float *y, // the things we can change to fix the ratio
-                    Vec2f ref_pt)       // the (fixed) reference point of the rect
+void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
 {
-    trace_assert(x);
-    trace_assert(y);
+    trace_assert(layer);
+    trace_assert(layer->selection >= 0);
+    trace_assert(a);
 
-    // if we're not holding down shift, don't bother.
-    if (!(SDL_GetKeyboardState(NULL)[SDL_SCANCODE_LSHIFT] || SDL_GetKeyboardState(NULL)[SDL_SCANCODE_RSHIFT]))
-        return;
+    if (!layer->snapping_enabled) return;
 
-    float ratio = layer->initial_rectangle_size.x / layer->initial_rectangle_size.y;
+    Rect *rects = (Rect*)layer->rects.data;
+    size_t rects_size = layer->rects.count;
 
-    // if we are holding down control also, then make squares.
-    if (SDL_GetKeyboardState(NULL)[SDL_SCANCODE_LCTRL] || SDL_GetKeyboardState(NULL)[SDL_SCANCODE_RCTRL])
-        ratio = 1.0f;
+    for (size_t i = 0; i < rects_size; ++i) {
+        if (i == (size_t) layer->selection) continue;
 
-    // make some constants for us to use.
-    float inv_ratio = 1.0f / ratio;
-    float w = *x - ref_pt.x;
-    float h = *y - ref_pt.y;
+        const Rect b = rects[i];
 
-    float ab_w = fabsf(w);
-    float ab_h = fabsf(h);
+        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);
+        }
 
-    // note: copysign takes (magnitude, sign). this thing basically lengthens the shorter side
-    // to fit the ratio. copysign is used to handle when the length is negative due to the
-    // different corner positions.
-    if (ab_w <= ratio * ab_h) {
-        *x = ref_pt.x + (ratio * copysignf(h, w));
-    } else if (ab_h <= inv_ratio * ab_w) {
-        *y = ref_pt.y + inv_ratio * copysignf(w, h);
+        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);
+        }
     }
 }
 
@@ -589,7 +714,7 @@ static int rect_layer_event_resize(RectLayer *layer,
     trace_assert(camera);
     trace_assert(layer->selection >= 0);
 
-    Rect *rects = dynarray_data(layer->rects);
+    Rect *rects = (Rect*)layer->rects.data;
 
     float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
 
@@ -602,223 +727,127 @@ static int rect_layer_event_resize(RectLayer *layer,
 
         switch (layer->resize_mask) {
         case 1: {               // TOP
-            float y = position.y;
-            float x = rects[layer->selection].x;
-            float w = rects[layer->selection].w;
-            for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
-                if (i == (size_t) layer->selection) continue;
-
-                const Rect b = rects[i];
-                if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
-                    snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold);
-                }
-            }
+            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(x, y),
+                vec(a.x, a.y),
                 rect_position2(rects[layer->selection]));
         } break;
 
         case 2: {               // LEFT
-            float y = rects[layer->selection].y;
-            float x = position.x;
-            float h = rects[layer->selection].h;
-            for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
-                if (i == (size_t) layer->selection) continue;
-
-                const Rect b = rects[i];
-                if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
-                    snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold);
-                }
-            }
+            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(x, y),
+                vec(a.x, a.y),
                 rect_position2(rects[layer->selection]));
         } break;
 
         case 3: {               // TOP,LEFT
-            float x = position.x;
-            float y = position.y;
-            float w = rects[layer->selection].w;
-            float h = rects[layer->selection].h;
-
-            // use the bottom-right as reference.
-            Vec2f ref_pos = rect_position2(rects[layer->selection]);
-            fix_rect_ratio(layer, &x, &y, ref_pos);
-
-            for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
-                if (i == (size_t) layer->selection) continue;
-
-                const Rect b = rects[i];
-                if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
-                    if (snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold)) {
-                        // if we did a snap, we need to update the rect to make sure it
-                        // still fits the ratio. same pattern repeats below.
-                        fix_rect_ratio(layer, &x, &y, ref_pos);
-                    }
-                }
+            Rect a = rect(
+                position.x,
+                position.y,
+                rects[layer->selection].w,
+                rects[layer->selection].h);
 
-                if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
-                    if (snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold)) {
-                        fix_rect_ratio(layer, &x, &y, ref_pos);
-                    }
-                }
-            }
+            snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
 
             layer->inter_rect = rect_from_points(
-                vec(x, y),
+                vec(a.x, a.y),
                 rect_position2(rects[layer->selection]));
         } break;
 
         case 4: {               // BOTTOM
-            float y = position.y;
-            float x = rects[layer->selection].x;
-            float w = rects[layer->selection].w;
-            for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
-                if (i == (size_t) layer->selection) continue;
-
-                const Rect b = rects[i];
-                if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
-                    snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold);
-                }
-            }
+            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,
-                    y));
+                    a.y));
         } break;
 
         case 6: {               // BOTTOM,LEFT
-            float x = position.x;
-            float y = position.y;
-            float w = rects[layer->selection].w;
-            float h = rects[layer->selection].h;
-
-            // use the top-right as reference.
-            Vec2f ref_pos = vec(rects[layer->selection].x + rects[layer->selection].w, rects[layer->selection].y);
-            fix_rect_ratio(layer, &x, &y, ref_pos);
-
-            for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
-                if (i == (size_t) layer->selection) continue;
-
-                const Rect b = rects[i];
-                if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
-                    if (snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold)) {
-                        fix_rect_ratio(layer, &x, &y, ref_pos);
-                    }
-                }
+            Rect a = rect(
+                position.x,
+                position.y,
+                rects[layer->selection].w,
+                -rects[layer->selection].h);
 
-                if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
-                    if (snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold)) {
-                        fix_rect_ratio(layer, &x, &y, ref_pos);
-                    }
-                }
-            }
+            snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
 
             layer->inter_rect = rect_from_points(
-                vec(x, rects[layer->selection].y),
+                vec(a.x, rects[layer->selection].y),
                 vec(rects[layer->selection].x + rects[layer->selection].w,
-                    y));
+                    a.y));
         } break;
 
         case 8: {               // RIGHT
-            float y = rects[layer->selection].y;
-            float x = position.x;
-            float h = rects[layer->selection].h;
-
-            for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
-                if (i == (size_t) layer->selection) continue;
+            Rect a = rect(position.x,
+                          rects[layer->selection].y,
+                          rects[layer->selection].w,
+                          rects[layer->selection].h);
 
-                const Rect b = rects[i];
-                if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
-                    snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold);
-                }
-            }
+            snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
 
             layer->inter_rect = rect_from_points(
                 rect_position(rects[layer->selection]),
-                vec(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
-            float x = position.x;
-            float y = position.y;
-            float w = rects[layer->selection].w;
-            float h = rects[layer->selection].h;
-
-            // use bottom-left as reference.
-            Vec2f ref_pos = vec(rects[layer->selection].x, rects[layer->selection].y + rects[layer->selection].h);
-            fix_rect_ratio(layer, &x, &y, ref_pos);
-
-            for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
-                if (i == (size_t) layer->selection) continue;
-
-                const Rect b = rects[i];
-                if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
-                    if (snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold)) {
-                        fix_rect_ratio(layer, &x, &y, ref_pos);
-                    }
-                }
+            Rect a = rect(
+                position.x,
+                position.y,
+                -rects[layer->selection].w,
+                rects[layer->selection].h);
 
-                if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
-                    if (snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold)) {
-                        fix_rect_ratio(layer, &x, &y, ref_pos);
-                    }
-                }
-            }
+            snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
 
             layer->inter_rect = rect_from_points(
-                vec(rects[layer->selection].x, y),
-                vec(x,
+                vec(rects[layer->selection].x, a.y),
+                vec(a.x,
                     rects[layer->selection].y + rects[layer->selection].h));
         } break;
 
         case 12: {              // BOTTOM,RIGHT
-            float x = position.x;
-            float y = position.y;
-            float w = rects[layer->selection].w;
-            float h = rects[layer->selection].h;
-
-            // use top-left as reference.
-            Vec2f ref_pos = rect_position(rects[layer->selection]);
-            fix_rect_ratio(layer, &x, &y, ref_pos);
-
-            for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
-                if (i == (size_t) layer->selection) continue;
-
-                const Rect b = rects[i];
-                if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
-                    if (snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold)) {
-                        fix_rect_ratio(layer, &x, &y, ref_pos);
-                    }
-                }
+            Rect a = rect(
+                position.x,
+                position.y,
+                -rects[layer->selection].w,
+                -rects[layer->selection].h);
 
-                if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
-                    if (snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold)) {
-                        fix_rect_ratio(layer, &x, &y, ref_pos);
-                    }
-                }
-            }
+            snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
 
             layer->inter_rect = rect_from_points(
                 rect_position(rects[layer->selection]),
-                vec(x, y));
+                vec(a.x, a.y));
         } break;
         }
 
-        // note that we need to update the "initial size" even during the drag. this is because
-        // we can enter/exit the ratio mode in the middle of dragging!
-        layer->initial_rectangle_size = vec(layer->inter_rect.w, layer->inter_rect.h);
-
     } break;
 
     case SDL_MOUSEBUTTONUP: {
         layer->state = RECT_LAYER_IDLE;
-        UNDO_PUSH(undo_history, create_undo_update_context(layer));
-        dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
+        RECT_UNDO_PUSH(
+            undo_history,
+            create_rect_undo_update_context(
+                layer,
+                (size_t) layer->selection));
+        dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
     } break;
     }
 
@@ -826,15 +855,20 @@ static int rect_layer_event_resize(RectLayer *layer,
 }
 
 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 = (Rect*)layer->rects.data;
+    size_t rects_size = layer->rects.count;
 
     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];
 
@@ -858,7 +892,7 @@ static int rect_layer_event_move(RectLayer *layer,
     trace_assert(camera);
     trace_assert(layer->selection >= 0);
 
-    Rect *rects = dynarray_data(layer->rects);
+    Rect *rects = (Rect*)layer->rects.data;
 
     switch (event->type) {
     case SDL_MOUSEMOTION: {
@@ -888,10 +922,8 @@ static int rect_layer_event_move(RectLayer *layer,
             }
         }
 
-        // TODO(#1141): Rect Snapping in Level Editor should be optional
-        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: {
@@ -902,8 +934,14 @@ 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));
-            dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
+            RECT_UNDO_PUSH(
+                undo_history,
+                create_rect_undo_update_context(
+                    layer, (size_t) layer->selection));
+            dynarray_replace_at(
+                &layer->rects,
+                (size_t) layer->selection,
+                &layer->inter_rect);
         }
     } break;
     }
@@ -924,11 +962,15 @@ 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,
+                    (size_t) layer->selection));
 
-            char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
+            char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
             memset(id, 0, ENTITY_MAX_ID_SIZE);
-            memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
+            memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
             layer->state = RECT_LAYER_IDLE;
             SDL_StopTextInput();
         } break;
@@ -941,7 +983,7 @@ static int rect_layer_event_id_rename(RectLayer *layer,
     } break;
     }
 
-    return edit_field_event(layer->id_edit_field, event);
+    return edit_field_event(&layer->id_edit_field, event);
 }
 
 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
@@ -953,173 +995,121 @@ LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
     return layer;
 }
 
-RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
+RectLayer *create_rect_layer(Memory *memory,
+                             const char *id_name_prefix,
+                             Cursor *cursor)
 {
+    trace_assert(memory);
+    trace_assert(id_name_prefix);
     trace_assert(cursor);
 
-    Lt *lt = create_lt();
+    RectLayer *rect_layer = memory_alloc(memory, sizeof(RectLayer));
 
-    RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
-    if (layer == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-    layer->lt = lt;
-
-    layer->ids = PUSH_LT(
-        lt,
-        create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
-        destroy_dynarray);
-    if (layer->ids == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-
-    layer->rects = PUSH_LT(
-        lt,
-        create_dynarray(sizeof(Rect)),
-        destroy_dynarray);
-    if (layer->rects == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-
-    layer->colors = PUSH_LT(
-        lt,
-        create_dynarray(sizeof(Color)),
-        destroy_dynarray);
-    if (layer->colors == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-
-    layer->actions = PUSH_LT(
-        lt,
-        create_dynarray(sizeof(Action)),
-        destroy_dynarray);
-    if (layer->actions == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-
-    layer->id_edit_field = PUSH_LT(
-        lt,
-        create_edit_field(
-            RECT_LAYER_ID_LABEL_SIZE,
-            COLOR_BLACK),
-        destroy_edit_field);
-    if (layer->id_edit_field == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-
-    layer->grid =
-        PUSH_LT(
-            lt,
-            nth_calloc(
-                1,
-                sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
-            free);
-    if (layer->grid == NULL) {
-        RETURN_LT(lt, NULL);
-    }
-    layer->grid->rows = RECT_LAYER_GRID_ROWS;
-    layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
-    grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
+    rect_layer->ids = create_dynarray(memory, sizeof(char) * ENTITY_MAX_ID_SIZE);
+    rect_layer->rects = create_dynarray(memory, sizeof(Rect));
+    rect_layer->colors = create_dynarray(memory, sizeof(Color));
+    rect_layer->actions = create_dynarray(memory, sizeof(Action));
+    rect_layer->id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
+    rect_layer->id_edit_field.font_color = COLOR_BLACK;
+    rect_layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
+    rect_layer->selection = -1;
+    rect_layer->id_name_prefix = id_name_prefix;
+    rect_layer->cursor = cursor;
 
-    layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
-    layer->selection = -1;
-    layer->id_name_prefix = id_name_prefix;
-    layer->cursor = cursor;
-
-    return layer;
+    return rect_layer;
 }
 
-RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream,
-                                              const char *id_name_prefix,
-                                              Cursor *cursor)
+void rect_layer_load(RectLayer *layer, Memory *memory, String *input)
 {
-    trace_assert(line_stream);
-
-    RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
-    if (layer == NULL) {
-        return NULL;
-    }
-
-    const char *line = line_stream_next(line_stream);
-    if (line == NULL) {
-        RETURN_LT(layer->lt, NULL);
-    }
-
-    size_t count = 0;
-    if (sscanf(line, "%zu", &count) < 0) {
-        RETURN_LT(layer->lt, NULL);
-    }
-
-    for (size_t i = 0; i < count; ++i) {
-        line = line_stream_next(line_stream);
-        if (line == NULL) {
-            RETURN_LT(layer->lt, NULL);
-        }
+    trace_assert(layer);
+    trace_assert(memory);
+    trace_assert(input);
 
-        char hex[7];
+    int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
+    char id[ENTITY_MAX_ID_SIZE];
+    for (int i = 0; i < n; ++i) {
         Rect rect;
-        char id[ENTITY_MAX_ID_SIZE];
-
-        int n = 0;
-        if (sscanf(line,
-                   "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
-                   id,
-                   &rect.x, &rect.y,
-                   &rect.w, &rect.h,
-                   hex, &n) <= 0) {
-            log_fail("%s\n", strerror(errno));
-            RETURN_LT(layer->lt, NULL);
-        }
-        line += n;
-
-        Color color = hexstr(hex);
-        dynarray_push(layer->rects, &rect);
-        dynarray_push(layer->ids, id);
-        dynarray_push(layer->colors, &color);
+        String line = trim(chop_by_delim(input, '\n'));
+        String string_id = trim(chop_word(&line));
+        rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+        rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+        rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+        rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+        Color color = hexs(trim(chop_word(&line)));
+
+        memset(id, 0, ENTITY_MAX_ID_SIZE);
+        memcpy(
+            id,
+            string_id.data,
+            min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
+
+        dynarray_push(&layer->rects, &rect);
+        dynarray_push(&layer->colors, &color);
+        dynarray_push(&layer->ids, id);
 
         Action action = {
             .type = ACTION_NONE,
             .entity_id = {0}
         };
 
-        if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
-            line += n;
+        String action_string = trim(chop_word(&line));
+        if (action_string.count > 0) {
+            action.type = (ActionType)atol(string_to_cstr(memory, action_string));
             switch (action.type) {
             case ACTION_NONE: break;
-
             case ACTION_TOGGLE_GOAL:
             case ACTION_HIDE_LABEL: {
-                if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
-                    log_fail("%s\n", strerror(errno));
-                    RETURN_LT(layer->lt, NULL);
-                }
+                String label_id = trim(chop_word(&line));
+                trace_assert(label_id.count > 0);
+                memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
+                memcpy(action.entity_id,
+                       label_id.data,
+                       min_size_t(
+                           ENTITY_MAX_ID_SIZE - 1,
+                           label_id.count));
             } break;
 
             case ACTION_N: break;
             }
         }
 
-        dynarray_push(layer->actions, &action);
+        dynarray_push(&layer->actions, &action);
     }
-
-    return layer;
 }
 
-void destroy_rect_layer(RectLayer *layer)
+static
+void render_tool_bar_button(const Camera *camera,
+                            Rect button_rect, const char *text,
+                            Color background_color,
+                            Color foreground_color)
 {
-    trace_assert(layer);
-    RETURN_LT0(layer->lt);
+    const Vec2f text_size = vec(5.0f, 5.0f);
+    const Rect text_rect = sprite_font_boundary_box(
+        vec(0.0f, 0.0f), text_size, text);
+    camera_fill_rect_screen(
+        camera,
+        button_rect,
+        background_color);
+    camera_render_text_screen(
+        camera,
+        text,
+        text_size,
+        foreground_color,
+        vec(
+            button_rect.x + (button_rect.w - text_rect.w) * 0.5f,
+            button_rect.y + (button_rect.h - text_rect.h) * 0.5f));
 }
 
+
 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
 {
     trace_assert(layer);
     trace_assert(camera);
 
-    const size_t n = dynarray_count(layer->rects);
-    Rect *rects = dynarray_data(layer->rects);
-    Color *colors = dynarray_data(layer->colors);
-    const char *ids = dynarray_data(layer->ids);
+    const size_t n = layer->rects.count;
+    Rect *rects = (Rect *)layer->rects.data;
+    Color *colors = (Color *)layer->colors.data;
+    const char *ids = (const char *)layer->ids.data;
 
     // The Rectangles
     for (size_t i = 0; i < n; ++i) {
@@ -1185,7 +1175,7 @@ int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
         if (layer->state == RECT_LAYER_ID_RENAME) {
             // ID renaming Edit Field
             if (edit_field_render_world(
-                    layer->id_edit_field,
+                    &layer->id_edit_field,
                     camera,
                     rect_id_pos) < 0) {
                 return -1;
@@ -1211,10 +1201,29 @@ int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
         }
     }
 
+    if (layer->state == RECT_LAYER_SUBTRACT) {
+        if (camera_draw_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
+            return -1;
+        }
+    }
+
     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
         return -1;
     }
 
+    // Tool bar
+    if (active) {
+        render_tool_bar_button(
+            camera, subtract_tool_button_rect(camera), "/",
+            layer->subtract_enabled ? TOOL_BAR_BACKGROUND : TOOL_BAR_FOREGROUND,
+            layer->subtract_enabled ? TOOL_BAR_FOREGROUND : TOOL_BAR_BACKGROUND);
+
+        render_tool_bar_button(
+            camera, snapping_tool_button_rect(camera), "S",
+            layer->snapping_enabled ? TOOL_BAR_BACKGROUND : TOOL_BAR_FOREGROUND,
+            layer->snapping_enabled ? TOOL_BAR_FOREGROUND : TOOL_BAR_BACKGROUND);
+    }
+
     return 0;
 }
 
@@ -1239,8 +1248,12 @@ 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));
-            dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
+            RECT_UNDO_PUSH(
+                undo_history,
+                create_rect_undo_update_context(
+                    layer,
+                    (size_t)layer->selection));
+            dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
             layer->state = RECT_LAYER_IDLE;
         }
     }
@@ -1257,18 +1270,6 @@ int rect_layer_event(RectLayer *layer,
     trace_assert(event);
     trace_assert(undo_history);
 
-    switch (event->type) {
-    case SDL_WINDOWEVENT: {
-        switch (event->window.event) {
-        case SDL_WINDOWEVENT_RESIZED: {
-            grid_relayout(layer->grid, rect(0.0f, 0.0f,
-                                            (float) event->window.data1,
-                                            (float) event->window.data2));
-        } break;
-        }
-    } break;
-    }
-
     switch (layer->state) {
     case RECT_LAYER_IDLE:
         return rect_layer_event_idle(layer, event, camera, undo_history);
@@ -1287,6 +1288,9 @@ int rect_layer_event(RectLayer *layer,
 
     case RECT_LAYER_RECOLOR:
         return rect_layer_event_recolor(layer, event, camera, undo_history);
+
+    case RECT_LAYER_SUBTRACT:
+        return rect_layer_event_subtract(layer, event, camera, undo_history);
     }
 
 
@@ -1295,22 +1299,22 @@ int rect_layer_event(RectLayer *layer,
 
 size_t rect_layer_count(const RectLayer *layer)
 {
-    return dynarray_count(layer->rects);
+    return layer->rects.count;
 }
 
 const Rect *rect_layer_rects(const RectLayer *layer)
 {
-    return dynarray_data(layer->rects);
+    return (const Rect *)layer->rects.data;
 }
 
 const Color *rect_layer_colors(const RectLayer *layer)
 {
-    return dynarray_data(layer->colors);
+    return (const Color *)layer->colors.data;
 }
 
 const char *rect_layer_ids(const RectLayer *layer)
 {
-    return dynarray_data(layer->ids);
+    return (const char *)layer->ids.data;
 }
 
 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
@@ -1318,11 +1322,11 @@ 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);
-    Action *actions = dynarray_data(layer->actions);
+    size_t n = layer->ids.count;
+    char *ids = (char *)layer->ids.data;
+    Rect *rects = (Rect *)layer->rects.data;
+    Color *colors = (Color *)layer->colors.data;
+    Action *actions = (Action *)layer->actions.data;
 
     fprintf(filedump, "%zd\n", n);
     for (size_t i = 0; i < n; ++i) {
@@ -1351,5 +1355,5 @@ int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
 
 const Action *rect_layer_actions(const RectLayer *layer)
 {
-    return dynarray_data(layer->actions);
+    return (const Action *)layer->actions.data;
 }