Introduce subtract operation to RectLayer
parent
3741da7938
commit
e3461003c6
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue