#include "dynarray.h"
#include "game/camera.h"
-#include "system/line_stream.h"
#include "system/log.h"
-#include "system/lt.h"
#include "system/nth_alloc.h"
#include "system/stacktrace.h"
#include "system/str.h"
#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"
#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 point_clipboard = 0;
+static Color point_clipboard_color;
+
+// TODO(#1140): PointLayer does not support snapping
typedef enum {
- POINT_LAYER_IDLE = 0,
- POINT_LAYER_EDIT_ID,
- POINT_LAYER_MOVE
-} PointLayerState;
+ POINT_UNDO_ADD,
+ POINT_UNDO_DELETE,
+ POINT_UNDO_UPDATE,
+ POINT_UNDO_SWAP
+} PointUndoType;
-struct PointLayer
-{
- Lt *lt;
- PointLayerState state;
- Dynarray/*<Point>*/ *positions;
- Dynarray/*<Color>*/ *colors;
- Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
- Edit_field *edit_field;
- int selected;
- ColorPicker color_picker;
- Color prev_color;
- Point prev_position;
-};
+typedef struct {
+ PointUndoType type;
+ PointLayer *layer;
+ Vec2f position;
+ Color color;
+ char id[ID_MAX_SIZE];
+ size_t index;
+ size_t index2;
+} PointUndoContext;
-LayerPtr point_layer_as_layer(PointLayer *point_layer)
+static
+PointUndoContext create_point_undo_swap_context(PointLayer *point_layer,
+ size_t index, size_t index2)
{
- LayerPtr layer = {
- .type = LAYER_POINT,
- .ptr = point_layer
- };
- return layer;
+ trace_assert(point_layer);
+ trace_assert(index < point_layer->positions.count);
+ trace_assert(index2 < point_layer->positions.count);
+
+ PointUndoContext undo_context;
+ undo_context.type = POINT_UNDO_SWAP;
+ undo_context.layer = point_layer;
+ undo_context.index = index;
+ undo_context.index2 = index2;
+ return undo_context;
}
-PointLayer *create_point_layer(void)
+static
+PointUndoContext create_point_undo_context(PointLayer *point_layer,
+ PointUndoType type)
{
- Lt *lt = create_lt();
+ trace_assert(type != POINT_UNDO_SWAP);
- PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
- if (point_layer == NULL) {
- RETURN_LT(lt, NULL);
- }
- point_layer->lt = lt;
+ (void) create_point_undo_swap_context;
- point_layer->state = POINT_LAYER_IDLE;
+ PointUndoContext undo_context;
- point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
- if (point_layer->positions == NULL) {
- RETURN_LT(lt, NULL);
- }
+ size_t index =
+ type == POINT_UNDO_ADD
+ ? point_layer->positions.count - 1
+ : (size_t) point_layer->selection;
- point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
- if (point_layer->colors == NULL) {
- RETURN_LT(lt, NULL);
- }
+ 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;
- point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
- if (point_layer->ids == NULL) {
- RETURN_LT(lt, NULL);
- }
-
- point_layer->edit_field = PUSH_LT(
- lt,
- create_edit_field(
- POINT_LAYER_ID_TEXT_SIZE,
- POINT_LAYER_ID_TEXT_COLOR),
- destroy_edit_field);
- if (point_layer->edit_field == NULL) {
- RETURN_LT(lt, NULL);
- }
-
- return point_layer;
+ return undo_context;
}
-PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
+static
+void point_layer_undo(void *context, size_t context_size)
{
- trace_assert(line_stream);
-
- PointLayer *point_layer = create_point_layer();
+ trace_assert(context);
+ trace_assert(sizeof(PointUndoContext) == context_size);
+
+ PointUndoContext *undo_context = context;
+ PointLayer *point_layer = undo_context->layer;
+
+ switch (undo_context->type) {
+ case POINT_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;
- size_t count = 0;
- if (sscanf(
- line_stream_next(line_stream),
- "%zu",
- &count) == EOF) {
- log_fail("Could not read amount of points");
- RETURN_LT(point_layer->lt, NULL);
- }
+ case POINT_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;
- char color_name[7];
- char id[ID_MAX_SIZE];
- float x, y;
- for (size_t i = 0; i < count; ++i) {
- if (sscanf(
- line_stream_next(line_stream),
- "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
- id, &x, &y, color_name) < 0) {
- log_fail("Could not read %dth goal\n", i);
- RETURN_LT(point_layer->lt, NULL);
- }
- const Color color = hexstr(color_name);
- const Point point = vec(x, y);
+ case POINT_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;
- dynarray_push(point_layer->colors, &color);
- dynarray_push(point_layer->positions, &point);
- dynarray_push(point_layer->ids, id);
+ case POINT_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;
}
+}
- point_layer->selected = -1;
+#define POINT_UNDO_PUSH(HISTORY, CONTEXT) \
+ do { \
+ PointUndoContext context = (CONTEXT); \
+ undo_history_push( \
+ HISTORY, \
+ point_layer_undo, \
+ &context, \
+ sizeof(context)); \
+ } while(0)
- point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
- point_layer->prev_color = COLOR_RED;
+LayerPtr point_layer_as_layer(PointLayer *point_layer)
+{
+ LayerPtr layer = {
+ .type = LAYER_POINT,
+ .ptr = point_layer
+ };
+ return layer;
+}
- return point_layer;
+PointLayer *create_point_layer(Memory *memory,
+ const char *id_name_prefix)
+{
+ trace_assert(memory);
+ trace_assert(id_name_prefix);
+
+ PointLayer *result = memory_alloc(memory, sizeof(PointLayer));
+ memset(result, 0, sizeof(PointLayer));
+ result->state = POINT_LAYER_IDLE;
+ result->positions = create_dynarray(memory, sizeof(Vec2f));
+ result->colors = create_dynarray(memory, sizeof(Color));
+ result->ids = create_dynarray(memory, sizeof(char) * ID_MAX_SIZE);
+ result->edit_field.font_size = POINT_LAYER_ID_TEXT_SIZE;
+ result->edit_field.font_color = POINT_LAYER_ID_TEXT_COLOR;
+ result->id_name_prefix = id_name_prefix;
+ return result;
}
-void destroy_point_layer(PointLayer *point_layer)
+void point_layer_load(PointLayer *point_layer,
+ Memory *memory,
+ String *input)
{
trace_assert(point_layer);
- RETURN_LT0(point_layer->lt);
+ trace_assert(memory);
+ trace_assert(input);
+
+ 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) {
+ String line = trim(chop_by_delim(input, '\n'));
+ String string_id = trim(chop_word(&line));
+ Vec2f point;
+ point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+ point.y = 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(&point_layer->positions, &point);
+ dynarray_push(&point_layer->colors, &color);
+ dynarray_push(&point_layer->ids, id);
+ }
+}
+
+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);
- Color *colors = dynarray_data(point_layer->colors);
- char *ids = dynarray_data(point_layer->ids);
+ const int n = (int)point_layer->positions.count;
+ Vec2f *positions = (Vec2f *)point_layer->positions.data;
+ Color *colors = (Color *)point_layer->colors.data;
+ char *ids = (char *)point_layer->ids.data;
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;
}
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,
+ &point_layer->edit_field,
camera,
- positions[point_layer->selected]) < 0) {
+ positions[point_layer->selection]) < 0) {
return -1;
}
}
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);
+ int n = (int) point_layer->positions.count;
+ Vec2f *positions = (Vec2f *)point_layer->positions.data;
- 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;
}
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)
{
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);
+ 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);
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(point_layer, POINT_UNDO_ADD));
return 0;
}
-typedef struct {
- Point position;
- Color color;
- char id[ID_MAX_SIZE];
- size_t index;
-} DeleteContext;
-
static
-void point_layer_revert_delete(void *layer, Context context)
+void point_layer_swap_elements(PointLayer *point_layer,
+ size_t a, size_t b,
+ UndoHistory *undo_history)
{
- trace_assert(layer);
- PointLayer *point_layer = layer;
+ trace_assert(point_layer);
+ trace_assert(undo_history);
+ trace_assert(a < point_layer->positions.count);
+ trace_assert(b < point_layer->positions.count);
- trace_assert(sizeof(DeleteContext) <= CONTEXT_SIZE);
- DeleteContext *delete_context = (DeleteContext *)context.data;
+ dynarray_swap(&point_layer->positions, a, b);
+ dynarray_swap(&point_layer->colors, a, b);
+ dynarray_swap(&point_layer->ids, a, b);
- dynarray_insert_before(point_layer->positions, delete_context->index, &delete_context->position);
- dynarray_insert_before(point_layer->colors, delete_context->index, &delete_context->color);
- dynarray_insert_before(point_layer->ids, delete_context->index, delete_context->id);
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_swap_context(point_layer, a, b));
}
static
{
trace_assert(point_layer);
- Action action = {
- .revert = point_layer_revert_delete,
- .layer = point_layer
- };
-
- trace_assert(sizeof(DeleteContext) <= CONTEXT_SIZE);
- DeleteContext *delete_context = (DeleteContext *)action.context.data;
-
- Point *positions = dynarray_data(point_layer->positions);
- Color *colors = dynarray_data(point_layer->colors);
- char *ids = dynarray_data(point_layer->ids);
-
- delete_context->position = positions[i];
- delete_context->color = colors[i];
- memcpy(
- delete_context->id,
- ids + i * ID_MAX_SIZE,
- ID_MAX_SIZE);
- delete_context->index = i;
-
- undo_history_push(undo_history, action);
-
- dynarray_delete_at(point_layer->positions, i);
- dynarray_delete_at(point_layer->colors, i);
- dynarray_delete_at(point_layer->ids, i);
-}
-
-typedef struct {
- size_t index;
- Color color;
-} ColorContext;
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(
+ point_layer,
+ POINT_UNDO_DELETE));
-static
-void point_layer_revert_color(void *layer, Context context)
-{
- log_info("point_layer_revert_color\n");
-
- trace_assert(layer);
- PointLayer *point_layer = layer;
-
- trace_assert(sizeof(ColorContext) <= CONTEXT_SIZE);
- ColorContext *color_context = (ColorContext*)context.data;
-
- const size_t n = dynarray_count(point_layer->colors);
- Color *colors = dynarray_data(point_layer->colors);
- trace_assert(color_context->index < n);
-
- colors[color_context->index] = color_context->color;
+ dynarray_delete_at(&point_layer->positions, i);
+ dynarray_delete_at(&point_layer->colors, i);
+ dynarray_delete_at(&point_layer->ids, i);
}
static
}
if (selected) {
- if (point_layer->selected >= 0) {
- Color *colors = dynarray_data(point_layer->colors);
-
- if (!color_picker_drag(&point_layer->color_picker)) {
- Action action = {
- .layer = point_layer,
- .revert = point_layer_revert_color
- };
-
- *((ColorContext*)action.context.data) = (ColorContext) {
- .index = (size_t) point_layer->selected,
- .color = point_layer->prev_color
- };
-
- undo_history_push(undo_history, action);
-
- point_layer->prev_color =
- color_picker_rgba(&point_layer->color_picker);
- }
-
- 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;
}
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,
color_picker_rgba(&point_layer->color_picker),
undo_history);
} else {
- Color *colors = dynarray_data(point_layer->colors);
- Point *positions = dynarray_data(point_layer->positions);
+ Color *colors = (Color*)point_layer->colors.data;
+ Vec2f *positions = (Vec2f*)point_layer->positions.data;
point_layer->state = POINT_LAYER_MOVE;
point_layer->color_picker =
- create_color_picker_from_rgba(colors[point_layer->selected]);
-
- point_layer->prev_color = colors[point_layer->selected];
- point_layer->prev_position = positions[point_layer->selected];
+ create_color_picker_from_rgba(colors[point_layer->selection]);
+ point_layer->inter_position = positions[point_layer->selection];
}
} break;
}
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) < point_layer->positions.count)) {
+ 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 < point_layer->positions.count)) {
+ 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)) {
+ if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
point_layer_delete_nth_element(
point_layer,
- (size_t)point_layer->selected,
+ (size_t)point_layer->selection,
undo_history);
- point_layer->selected = -1;
+ point_layer->selection = -1;
}
} break;
case SDLK_F2: {
- if (point_layer->selected >= 0) {
- char *ids = dynarray_data(point_layer->ids);
+ if (point_layer->selection >= 0) {
+ char *ids = (char*)point_layer->ids.data;
point_layer->state = POINT_LAYER_EDIT_ID;
edit_field_replace(
- point_layer->edit_field,
- ids + ID_MAX_SIZE * point_layer->selected);
+ &point_layer->edit_field,
+ ids + ID_MAX_SIZE * point_layer->selection);
SDL_StartTextInput();
}
} break;
+
+ case SDLK_c: {
+ if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
+ point_clipboard = 1;
+ dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
+ }
+ } break;
+
+ case SDLK_v: {
+ if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+ Vec2f position = camera_map_screen(camera, x, y);
+
+ point_layer_add_element(
+ point_layer,
+ position,
+ point_clipboard_color,
+ undo_history);
+ }
+ } break;
}
} break;
}
return 0;
}
-typedef struct {
- size_t index;
- char id[ID_MAX_SIZE];
-} RenameContext;
-
-static
-void point_layer_revert_rename(void *layer,
- Context context)
-{
- trace_assert(layer);
- PointLayer *point_layer = layer;
-
- ASSERT_CONTEXT_SIZE(RenameContext);
- RenameContext *rename_context = (RenameContext *)context.data;
-
- trace_assert(rename_context->index < dynarray_count(point_layer->ids));
-
- char *ids = dynarray_data(point_layer->ids);
- memcpy(
- ids + rename_context->index * ID_MAX_SIZE,
- rename_context->id,
- ID_MAX_SIZE);
-}
-
static
int point_layer_edit_id_event(PointLayer *point_layer,
const SDL_Event *event,
case SDL_KEYDOWN: {
switch(event->key.keysym.sym) {
case SDLK_RETURN: {
- char *ids = dynarray_data(point_layer->ids);
- const char *text = edit_field_as_text(point_layer->edit_field);
-
- Action action = {
- .revert = point_layer_revert_rename,
- .layer = point_layer
- };
-
- ASSERT_CONTEXT_SIZE(RenameContext);
- RenameContext *rename_context = (RenameContext *)action.context.data;
-
- memcpy(
- rename_context->id,
- ids + point_layer->selected * ID_MAX_SIZE,
- ID_MAX_SIZE);
- rename_context->index = (size_t) point_layer->selected;
-
- undo_history_push(undo_history, action);
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(
+ point_layer,
+ POINT_UNDO_UPDATE));
- 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';
+ 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 = 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();
} break;
}
- return edit_field_event(point_layer->edit_field, event);
-}
-
-typedef struct {
- size_t index;
- Point position;
-} MoveContext;
-
-static
-void point_layer_revert_move(void *layer, Context context)
-{
- trace_assert(layer);
- PointLayer *point_layer = layer;
-
- ASSERT_CONTEXT_SIZE(MoveContext);
- MoveContext *move_context = (MoveContext *)context.data;
-
- trace_assert(move_context->index < dynarray_count(point_layer->positions));
- Point *positions = dynarray_data(point_layer->positions);
- positions[move_context->index] = move_context->position;
+ return edit_field_event(&point_layer->edit_field, event);
}
static
trace_assert(point_layer);
trace_assert(event);
trace_assert(camera);
- trace_assert(point_layer->selected >= 0);
+ trace_assert(point_layer->selection >= 0);
+
+ Vec2f *positions = (Vec2f*)point_layer->positions.data;
switch (event->type) {
case SDL_MOUSEBUTTONUP: {
case SDL_BUTTON_LEFT: {
point_layer->state = POINT_LAYER_IDLE;
- Action action = {
- .revert = point_layer_revert_move,
- .layer = point_layer
- };
-
- MoveContext *context = (MoveContext *)action.context.data;
- ASSERT_CONTEXT_SIZE(MoveContext);
-
- context->index = (size_t) point_layer->selected;
- context->position = point_layer->prev_position;
-
- // TODO(#1014): just click (without moving) on the point creates an undo history entry
- undo_history_push(undo_history, action);
+ const float distance = vec_length(
+ vec_sub(point_layer->inter_position,
+ positions[point_layer->selection]));
+
+ if (distance > 1e-6) {
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(
+ point_layer,
+ POINT_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)) {
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(
+ point_layer,
+ POINT_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,
case POINT_LAYER_MOVE:
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;
size_t point_layer_count(const PointLayer *point_layer)
{
trace_assert(point_layer);
- return dynarray_count(point_layer->positions);
+ return point_layer->positions.count;
}
-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);
+ return (const Vec2f *)point_layer->positions.data;
}
const Color *point_layer_colors(const PointLayer *point_layer)
{
trace_assert(point_layer);
- return dynarray_data(point_layer->colors);
+ return (const Color *)point_layer->colors.data;
}
const char *point_layer_ids(const PointLayer *point_layer)
{
trace_assert(point_layer);
- return dynarray_data(point_layer->ids);
+ return (const char *)point_layer->ids.data;
}
int point_layer_dump_stream(const PointLayer *point_layer,
trace_assert(point_layer);
trace_assert(filedump);
- size_t n = dynarray_count(point_layer->ids);
- char *ids = dynarray_data(point_layer->ids);
- Point *positions = dynarray_data(point_layer->positions);
- Color *colors = dynarray_data(point_layer->colors);
+ size_t n = point_layer->ids.count;
+ char *ids = (char *) point_layer->ids.data;
+ Vec2f *positions = (Vec2f *) point_layer->positions.data;
+ Color *colors = (Color *) point_layer->colors.data;
fprintf(filedump, "%zd\n", n);
for (size_t i = 0; i < n; ++i) {