X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fgame%2Flevel%2Flevel_editor%2Fpoint_layer.c;h=96f96d1c7985361996dfa01523c5b1e3b86921f9;hb=8a286776bbb3a0323d34f53f677e07efad423dac;hp=7e59054fa791034f2852b04a2c74bfe996f4d79a;hpb=ad90ff737066c6bac3d4c51748ece1a99c172e8f;p=nothing.git diff --git a/src/game/level/level_editor/point_layer.c b/src/game/level/level_editor/point_layer.c index 7e59054f..96f96d1c 100644 --- a/src/game/level/level_editor/point_layer.c +++ b/src/game/level/level_editor/point_layer.c @@ -13,6 +13,7 @@ #include "ui/edit_field.h" #include "./point_layer.h" #include "math/extrema.h" +#include "math/mat3x3.h" #include "./color_picker.h" #include "undo_history.h" @@ -20,26 +21,142 @@ #define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f) #define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK -// TODO(#1002): PointLayer does not fully support UndoHistory +static int clipboard = 0; +static Color clipboard_color; + +// TODO(#1140): PointLayer does not support snapping typedef enum { POINT_LAYER_IDLE = 0, POINT_LAYER_EDIT_ID, - POINT_LAYER_MOVE + POINT_LAYER_MOVE, + POINT_LAYER_RECOLOR } PointLayerState; struct PointLayer { Lt *lt; PointLayerState state; - Dynarray/**/ *positions; + Dynarray/**/ *positions; Dynarray/**/ *colors; Dynarray/**/ *ids; - Edit_field *edit_field; - int selected; + int selection; ColorPicker color_picker; + + Vec2f inter_position; + Color inter_color; + Edit_field *edit_field; + + int id_name_counter; + const char *id_name_prefix; }; +typedef enum { + UNDO_ADD, + UNDO_DELETE, + UNDO_UPDATE, + UNDO_SWAP +} UndoType; + +typedef struct { + UndoType type; + PointLayer *layer; + Vec2f position; + Color color; + char id[ID_MAX_SIZE]; + size_t index; + size_t index2; +} UndoContext; + +static +UndoContext create_undo_swap_context(PointLayer *point_layer, + size_t index, size_t index2) +{ + trace_assert(point_layer); + trace_assert(index < dynarray_count(point_layer->positions)); + trace_assert(index2 < dynarray_count(point_layer->positions)); + + UndoContext undo_context; + undo_context.type = UNDO_SWAP; + undo_context.layer = point_layer; + undo_context.index = index; + undo_context.index2 = index2; + return undo_context; +} + +static +UndoContext create_undo_context(PointLayer *point_layer, + UndoType type) +{ + trace_assert(type != UNDO_SWAP); + + (void) create_undo_swap_context; + + UndoContext undo_context; + + size_t index = + type == UNDO_ADD + ? dynarray_count(point_layer->positions) - 1 + : (size_t) point_layer->selection; + + undo_context.type = type; + undo_context.layer = point_layer; + dynarray_copy_to(point_layer->positions, &undo_context.position, index); + dynarray_copy_to(point_layer->colors, &undo_context.color, index); + dynarray_copy_to(point_layer->ids, &undo_context.id, index); + undo_context.index = index; + + return undo_context; +} + +static +void point_layer_undo(void *context, size_t context_size) +{ + trace_assert(context); + trace_assert(sizeof(UndoContext) == context_size); + + UndoContext *undo_context = context; + PointLayer *point_layer = undo_context->layer; + + switch (undo_context->type) { + case UNDO_ADD: { + dynarray_pop(point_layer->positions, NULL); + dynarray_pop(point_layer->colors, NULL); + dynarray_pop(point_layer->ids, NULL); + point_layer->selection = -1; + } break; + + case UNDO_DELETE: { + dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position); + dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color); + dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id); + point_layer->selection = -1; + } break; + + case UNDO_UPDATE: { + dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position); + dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color); + dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id); + } break; + + case UNDO_SWAP: { + dynarray_swap(point_layer->positions, undo_context->index, undo_context->index2); + dynarray_swap(point_layer->colors, undo_context->index, undo_context->index2); + dynarray_swap(point_layer->ids, undo_context->index, undo_context->index2); + } break; + } +} + +#define UNDO_PUSH(HISTORY, CONTEXT) \ + do { \ + UndoContext context = (CONTEXT); \ + undo_history_push( \ + HISTORY, \ + point_layer_undo, \ + &context, \ + sizeof(context)); \ + } while(0) + LayerPtr point_layer_as_layer(PointLayer *point_layer) { LayerPtr layer = { @@ -49,7 +166,7 @@ LayerPtr point_layer_as_layer(PointLayer *point_layer) return layer; } -PointLayer *create_point_layer(void) +PointLayer *create_point_layer(const char *id_name_prefix) { Lt *lt = create_lt(); @@ -61,7 +178,7 @@ PointLayer *create_point_layer(void) point_layer->state = POINT_LAYER_IDLE; - point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray); + point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Vec2f)), destroy_dynarray); if (point_layer->positions == NULL) { RETURN_LT(lt, NULL); } @@ -86,14 +203,17 @@ PointLayer *create_point_layer(void) RETURN_LT(lt, NULL); } + point_layer->id_name_prefix = id_name_prefix; + return point_layer; } -PointLayer *create_point_layer_from_line_stream(LineStream *line_stream) +PointLayer *create_point_layer_from_line_stream(LineStream *line_stream, + const char *id_name_prefix) { trace_assert(line_stream); - PointLayer *point_layer = create_point_layer(); + PointLayer *point_layer = create_point_layer(id_name_prefix); size_t count = 0; if (sscanf( @@ -116,16 +236,16 @@ PointLayer *create_point_layer_from_line_stream(LineStream *line_stream) RETURN_LT(point_layer->lt, NULL); } const Color color = hexstr(color_name); - const Point point = vec(x, y); + const Vec2f point = vec(x, y); dynarray_push(point_layer->colors, &color); dynarray_push(point_layer->positions, &point); dynarray_push(point_layer->ids, id); } - point_layer->selected = -1; + point_layer->selection = -1; - point_layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f)); + point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED); return point_layer; } @@ -136,37 +256,48 @@ void destroy_point_layer(PointLayer *point_layer) RETURN_LT0(point_layer->lt); } +static inline +Triangle element_shape(Vec2f position, float scale) +{ + return triangle_mat3x3_product( + equilateral_triangle(), + mat3x3_product( + trans_mat_vec(position), + scale_mat(scale))); +} + int point_layer_render(const PointLayer *point_layer, - Camera *camera, + const Camera *camera, int active) { trace_assert(point_layer); trace_assert(camera); const int n = (int) dynarray_count(point_layer->positions); - Point *positions = dynarray_data(point_layer->positions); + Vec2f *positions = dynarray_data(point_layer->positions); Color *colors = dynarray_data(point_layer->colors); char *ids = dynarray_data(point_layer->ids); for (int i = 0; i < n; ++i) { - const Triangle t = triangle_mat3x3_product( - equilateral_triangle(), - mat3x3_product( - trans_mat(positions[i].x, positions[i].y), - scale_mat(POINT_LAYER_ELEMENT_RADIUS))); - const Color color = color_scale( - colors[i], + point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection + ? point_layer->inter_color + : colors[i], rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)); - if (i == point_layer->selected) { - const Triangle t0 = triangle_mat3x3_product( - equilateral_triangle(), - mat3x3_product( - trans_mat(positions[i].x, positions[i].y), - scale_mat(15.0f))); + const Vec2f position = + point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection + ? point_layer->inter_position + : positions[i]; - if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) { + // Selection Layer + if (active && i == point_layer->selection) { + if (camera_fill_triangle( + camera, + element_shape( + position, + POINT_LAYER_ELEMENT_RADIUS + 5.0f), + color_invert(color)) < 0) { return -1; } @@ -176,22 +307,26 @@ int point_layer_render(const PointLayer *point_layer, ids + ID_MAX_SIZE * i, POINT_LAYER_ID_TEXT_SIZE, POINT_LAYER_ID_TEXT_COLOR, - positions[i]) < 0) { + position) < 0) { return -1; } } - if (camera_fill_triangle(camera, t, color) < 0) { + if (camera_fill_triangle( + camera, + element_shape( + position, + POINT_LAYER_ELEMENT_RADIUS), + color) < 0) { return -1; } - } if (point_layer->state == POINT_LAYER_EDIT_ID) { if (edit_field_render_world( point_layer->edit_field, camera, - positions[point_layer->selected]) < 0) { + positions[point_layer->selection]) < 0) { return -1; } } @@ -206,14 +341,14 @@ int point_layer_render(const PointLayer *point_layer, static int point_layer_element_at(const PointLayer *point_layer, - Point position) + Vec2f position) { trace_assert(point_layer); int n = (int) dynarray_count(point_layer->positions); - Point *positions = dynarray_data(point_layer->positions); + Vec2f *positions = dynarray_data(point_layer->positions); - for (int i = 0; i < n; ++i) { + for (int i = n - 1; i >= 0; --i) { if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) { return i; } @@ -222,22 +357,9 @@ int point_layer_element_at(const PointLayer *point_layer, return -1; } -static -void point_layer_pop_element(void *layer, Context context) -{ - trace_assert(layer); - (void) context; - - PointLayer *point_layer = layer; - - dynarray_pop(point_layer->positions, NULL); - dynarray_pop(point_layer->colors, NULL); - dynarray_pop(point_layer->ids, NULL); -} - static int point_layer_add_element(PointLayer *point_layer, - Point position, + Vec2f position, Color color, UndoHistory *undo_history) { @@ -245,29 +367,53 @@ int point_layer_add_element(PointLayer *point_layer, trace_assert(undo_history); char id[ID_MAX_SIZE]; - for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) { - id[i] = (char) ('a' + rand() % ('z' - 'a' + 1)); - } - id[ID_MAX_SIZE - 1] = '\0'; + snprintf(id, ID_MAX_SIZE, "%s_%d", + point_layer->id_name_prefix, + point_layer->id_name_counter++); dynarray_push(point_layer->positions, &position); dynarray_push(point_layer->colors, &color); dynarray_push(point_layer->ids, id); - Action action = { - .revert = point_layer_pop_element, - .layer = point_layer - }; - undo_history_push(undo_history, action); + UNDO_PUSH( + undo_history, + create_undo_context(point_layer, UNDO_ADD)); return 0; } +static +void point_layer_swap_elements(PointLayer *point_layer, + size_t a, size_t b, + UndoHistory *undo_history) +{ + trace_assert(point_layer); + trace_assert(undo_history); + trace_assert(a < dynarray_count(point_layer->positions)); + trace_assert(b < dynarray_count(point_layer->positions)); + + dynarray_swap(point_layer->positions, a, b); + dynarray_swap(point_layer->colors, a, b); + dynarray_swap(point_layer->ids, a, b); + + UNDO_PUSH( + undo_history, + create_undo_swap_context(point_layer, a, b)); +} + static void point_layer_delete_nth_element(PointLayer *point_layer, - size_t i) + size_t i, + UndoHistory *undo_history) { trace_assert(point_layer); + + UNDO_PUSH( + undo_history, + create_undo_context( + point_layer, + UNDO_DELETE)); + dynarray_delete_at(point_layer->positions, i); dynarray_delete_at(point_layer->colors, i); dynarray_delete_at(point_layer->ids, i); @@ -287,17 +433,16 @@ int point_layer_idle_event(PointLayer *point_layer, if (color_picker_event( &point_layer->color_picker, event, + camera, &selected) < 0) { return -1; } if (selected) { - if (point_layer->selected >= 0) { - Color *colors = dynarray_data(point_layer->colors); - colors[point_layer->selected] = - color_picker_rgba(&point_layer->color_picker); + if (point_layer->selection >= 0) { + point_layer->inter_color = color_picker_rgba(&point_layer->color_picker); + point_layer->state = POINT_LAYER_RECOLOR; } - return 0; } @@ -305,12 +450,12 @@ int point_layer_idle_event(PointLayer *point_layer, case SDL_MOUSEBUTTONDOWN: { switch (event->button.button) { case SDL_BUTTON_LEFT: { - const Point position = camera_map_screen(camera, event->button.x, event->button.y); + const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y); - point_layer->selected = point_layer_element_at( + point_layer->selection = point_layer_element_at( point_layer, position); - if (point_layer->selected < 0) { + if (point_layer->selection < 0) { point_layer_add_element( point_layer, position, @@ -318,9 +463,12 @@ int point_layer_idle_event(PointLayer *point_layer, undo_history); } else { Color *colors = dynarray_data(point_layer->colors); + Vec2f *positions = dynarray_data(point_layer->positions); + point_layer->state = POINT_LAYER_MOVE; point_layer->color_picker = - create_color_picker_from_rgba(colors[point_layer->selected]); + create_color_picker_from_rgba(colors[point_layer->selection]); + point_layer->inter_position = positions[point_layer->selection]; } } break; } @@ -328,23 +476,73 @@ int point_layer_idle_event(PointLayer *point_layer, case SDL_KEYDOWN: { switch (event->key.keysym.sym) { + case SDLK_UP: { + if ((event->key.keysym.mod & KMOD_SHIFT) + && (point_layer->selection >= 0) + && ((size_t)(point_layer->selection + 1) < dynarray_count(point_layer->positions))) { + point_layer_swap_elements( + point_layer, + (size_t) point_layer->selection, + (size_t) point_layer->selection + 1, + undo_history); + point_layer->selection++; + } + } break; + + case SDLK_DOWN: { + if ((event->key.keysym.mod & KMOD_SHIFT) + && (point_layer->selection > 0) + && ((size_t) point_layer->selection < dynarray_count(point_layer->positions))) { + point_layer_swap_elements( + point_layer, + (size_t) point_layer->selection, + (size_t) point_layer->selection - 1, + undo_history); + point_layer->selection--; + } + } break; + case SDLK_DELETE: { - if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) { - point_layer_delete_nth_element(point_layer, (size_t)point_layer->selected); - point_layer->selected = -1; + if (0 <= point_layer->selection && point_layer->selection < (int) dynarray_count(point_layer->positions)) { + point_layer_delete_nth_element( + point_layer, + (size_t)point_layer->selection, + undo_history); + point_layer->selection = -1; } } break; case SDLK_F2: { - if (point_layer->selected >= 0) { + if (point_layer->selection >= 0) { char *ids = dynarray_data(point_layer->ids); point_layer->state = POINT_LAYER_EDIT_ID; edit_field_replace( point_layer->edit_field, - ids + ID_MAX_SIZE * point_layer->selected); + ids + ID_MAX_SIZE * point_layer->selection); SDL_StartTextInput(); } } break; + + case SDLK_c: { + if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) { + clipboard = 1; + dynarray_copy_to(point_layer->colors, &clipboard_color, (size_t)point_layer->selection); + } + } break; + + case SDLK_v: { + if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) { + int x, y; + SDL_GetMouseState(&x, &y); + Vec2f position = camera_map_screen(camera, x, y); + + point_layer_add_element( + point_layer, + position, + clipboard_color, + undo_history); + } + } break; } } break; } @@ -355,7 +553,8 @@ int point_layer_idle_event(PointLayer *point_layer, static int point_layer_edit_id_event(PointLayer *point_layer, const SDL_Event *event, - const Camera *camera) + const Camera *camera, + UndoHistory *undo_history) { trace_assert(point_layer); trace_assert(event); @@ -365,11 +564,18 @@ int point_layer_edit_id_event(PointLayer *point_layer, case SDL_KEYDOWN: { switch(event->key.keysym.sym) { case SDLK_RETURN: { - char *ids = dynarray_data(point_layer->ids); + UNDO_PUSH( + undo_history, + create_undo_context( + point_layer, + UNDO_UPDATE)); + + char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selection); const char *text = edit_field_as_text(point_layer->edit_field); - size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1); - memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n); - *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0'; + size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1); + memcpy(id, text, n); + memset(id + n, 0, ID_MAX_SIZE - n); + point_layer->state = POINT_LAYER_IDLE; SDL_StopTextInput(); return 0; @@ -390,32 +596,109 @@ int point_layer_edit_id_event(PointLayer *point_layer, static int point_layer_move_event(PointLayer *point_layer, const SDL_Event *event, - const Camera *camera) + const Camera *camera, + UndoHistory *undo_history) { trace_assert(point_layer); trace_assert(event); trace_assert(camera); - trace_assert(point_layer->selected >= 0); + trace_assert(point_layer->selection >= 0); + + Vec2f *positions = dynarray_data(point_layer->positions); switch (event->type) { case SDL_MOUSEBUTTONUP: { switch (event->button.button) { case SDL_BUTTON_LEFT: { point_layer->state = POINT_LAYER_IDLE; + + const float distance = vec_length( + vec_sub(point_layer->inter_position, + positions[point_layer->selection])); + + if (distance > 1e-6) { + UNDO_PUSH( + undo_history, + create_undo_context( + point_layer, + UNDO_UPDATE)); + + dynarray_replace_at( + point_layer->positions, + (size_t) point_layer->selection, + &point_layer->inter_position); + } } break; } } break; case SDL_MOUSEMOTION: { - Point *positions = dynarray_data(point_layer->positions); - positions[point_layer->selected] = - camera_map_screen(camera, event->motion.x, event->motion.y); + const Uint8 *state = SDL_GetKeyboardState(NULL); + const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y); + const Vec2f point_pos = positions[point_layer->selection]; + + if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) { + point_layer->inter_position = mouse_pos; + } else { + const float dx = fabsf(point_pos.x - mouse_pos.x); + const float dy = fabsf(point_pos.y - mouse_pos.y); + + if (dx > dy) { + point_layer->inter_position = vec(mouse_pos.x, point_pos.y); + } else { + point_layer->inter_position = vec(point_pos.x, mouse_pos.y); + } + } } break; } return 0; } +static +int point_layer_recolor_event(PointLayer *point_layer, + const SDL_Event *event, + const Camera *camera, + UndoHistory *undo_history) +{ + trace_assert(point_layer); + trace_assert(event); + trace_assert(camera); + trace_assert(undo_history); + trace_assert(point_layer->selection >= 0); + + int selected = 0; + if (color_picker_event( + &point_layer->color_picker, + event, + camera, + &selected) < 0) { + return -1; + } + + if (selected) { + point_layer->inter_color = color_picker_rgba(&point_layer->color_picker); + + if (!color_picker_drag(&point_layer->color_picker)) { + UNDO_PUSH( + undo_history, + create_undo_context( + point_layer, + UNDO_UPDATE)); + + dynarray_replace_at( + point_layer->colors, + (size_t) point_layer->selection, + &point_layer->inter_color); + + point_layer->state = POINT_LAYER_IDLE; + } + } + + + return 0; +} + int point_layer_event(PointLayer *point_layer, const SDL_Event *event, const Camera *camera, @@ -431,10 +714,13 @@ int point_layer_event(PointLayer *point_layer, return point_layer_idle_event(point_layer, event, camera, undo_history); case POINT_LAYER_EDIT_ID: - return point_layer_edit_id_event(point_layer, event, camera); + return point_layer_edit_id_event(point_layer, event, camera, undo_history); case POINT_LAYER_MOVE: - return point_layer_move_event(point_layer, event, camera); + return point_layer_move_event(point_layer, event, camera, undo_history); + + case POINT_LAYER_RECOLOR: + return point_layer_recolor_event(point_layer, event, camera, undo_history); } return 0; @@ -446,7 +732,7 @@ size_t point_layer_count(const PointLayer *point_layer) return dynarray_count(point_layer->positions); } -const Point *point_layer_positions(const PointLayer *point_layer) +const Vec2f *point_layer_positions(const PointLayer *point_layer) { trace_assert(point_layer); return dynarray_data(point_layer->positions); @@ -472,7 +758,7 @@ int point_layer_dump_stream(const PointLayer *point_layer, size_t n = dynarray_count(point_layer->ids); char *ids = dynarray_data(point_layer->ids); - Point *positions = dynarray_data(point_layer->positions); + Vec2f *positions = dynarray_data(point_layer->positions); Color *colors = dynarray_data(point_layer->colors); fprintf(filedump, "%zd\n", n);