Introduce subtract operation to RectLayer

master
rexim 2020-02-03 02:24:52 +07:00
parent 3741da7938
commit e3461003c6
2 changed files with 185 additions and 13 deletions

View File

@ -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);
}

View File

@ -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 {