From e3461003c6434ec87e064c1ed358b5857a749055 Mon Sep 17 00:00:00 2001 From: rexim Date: Mon, 3 Feb 2020 02:24:52 +0700 Subject: [PATCH] Introduce subtract operation to RectLayer --- src/game/level/level_editor/rect_layer.c | 195 +++++++++++++++++++++-- src/game/level/level_editor/rect_layer.h | 3 +- 2 files changed, 185 insertions(+), 13 deletions(-) diff --git a/src/game/level/level_editor/rect_layer.c b/src/game/level/level_editor/rect_layer.c index a12d4b18..92b5b58c 100644 --- a/src/game/level/level_editor/rect_layer.c +++ b/src/game/level/level_editor/rect_layer.c @@ -93,10 +93,9 @@ RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index) } static -RectUndoContext create_rect_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 < layer->rects.count); RectUndoContext undo_context; @@ -110,17 +109,17 @@ RectUndoContext create_rect_undo_element_context(RectLayer *layer) } static -RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer) +RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer, size_t index) { - RectUndoContext undo_context = create_rect_undo_element_context(rect_layer); + RectUndoContext undo_context = create_rect_undo_element_context(rect_layer, index); undo_context.type = RECT_UNDO_UPDATE; return undo_context; } static -RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer) +RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer, size_t i) { - RectUndoContext undo_context = create_rect_undo_element_context(rect_layer); + RectUndoContext undo_context = create_rect_undo_element_context(rect_layer, i); undo_context.type = RECT_UNDO_DELETE; return undo_context; } @@ -255,7 +254,7 @@ static int rect_layer_delete_rect_at_index(RectLayer *layer, { trace_assert(layer); - RECT_UNDO_PUSH(undo_history, create_rect_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); @@ -431,6 +430,14 @@ static int rect_layer_event_idle(RectLayer *layer, } } break; + case SDLK_x: { + int x, y; + SDL_GetMouseState(&x, &y); + layer->create_begin = camera_map_screen(camera, x, y); + layer->create_end = layer->create_begin; + layer->state = RECT_LAYER_SUBTRACT; + } break; + case SDLK_v: { if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) { int x, y; @@ -452,6 +459,143 @@ 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); + 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, @@ -660,7 +804,11 @@ static int rect_layer_event_resize(RectLayer *layer, case SDL_MOUSEBUTTONUP: { layer->state = RECT_LAYER_IDLE; - RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer)); + 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; } @@ -748,8 +896,14 @@ static int rect_layer_event_move(RectLayer *layer, rect_position(rects[layer->selection]))); if (distance > 1e-6) { - RECT_UNDO_PUSH(undo_history, create_rect_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; } @@ -770,7 +924,11 @@ static int rect_layer_event_id_rename(RectLayer *layer, case SDL_KEYDOWN: { switch (event->key.keysym.sym) { case SDLK_RETURN: { - RECT_UNDO_PUSH(undo_history, create_rect_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); memset(id, 0, ENTITY_MAX_ID_SIZE); @@ -981,6 +1139,12 @@ 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; } @@ -1009,7 +1173,11 @@ int rect_layer_event_recolor(RectLayer *layer, layer->inter_color = color_picker_rgba(&layer->color_picker); if (!color_picker_drag(&layer->color_picker)) { - RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer)); + 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; } @@ -1045,6 +1213,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); } diff --git a/src/game/level/level_editor/rect_layer.h b/src/game/level/level_editor/rect_layer.h index 77013ab3..8bb37b44 100644 --- a/src/game/level/level_editor/rect_layer.h +++ b/src/game/level/level_editor/rect_layer.h @@ -16,7 +16,8 @@ typedef enum { RECT_LAYER_RESIZE, RECT_LAYER_MOVE, RECT_LAYER_ID_RENAME, - RECT_LAYER_RECOLOR + RECT_LAYER_RECOLOR, + RECT_LAYER_SUBTRACT } RectLayerState; struct RectLayer { -- 2.44.0