#define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
#define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
-static int clipboard = 0;
-static Color clipboard_color;
+static int point_clipboard = 0;
+static Color point_clipboard_color;
+
+// TODO(#1140): PointLayer does not support snapping
typedef enum {
POINT_LAYER_IDLE = 0,
{
Lt *lt;
PointLayerState state;
- Dynarray/*<Point>*/ *positions;
+ Dynarray/*<Vec2f>*/ *positions;
Dynarray/*<Color>*/ *colors;
Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
- int selected;
+ int selection;
ColorPicker color_picker;
- Point inter_position;
+ 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
-} UndoType;
+ POINT_UNDO_ADD,
+ POINT_UNDO_DELETE,
+ POINT_UNDO_UPDATE,
+ POINT_UNDO_SWAP
+} PointUndoType;
typedef struct {
- UndoType type;
+ PointUndoType type;
PointLayer *layer;
- Point position;
+ Vec2f position;
Color color;
char id[ID_MAX_SIZE];
size_t index;
-} UndoContext;
+ size_t index2;
+} PointUndoContext;
+
+static
+PointUndoContext create_point_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));
+
+ 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;
+}
static
-UndoContext point_layer_create_undo_context(PointLayer *point_layer,
- UndoType type)
+PointUndoContext create_point_undo_context(PointLayer *point_layer,
+ PointUndoType type)
{
- UndoContext undo_context;
+ trace_assert(type != POINT_UNDO_SWAP);
+
+ (void) create_point_undo_swap_context;
+
+ PointUndoContext undo_context;
size_t index =
- type == UNDO_ADD
+ type == POINT_UNDO_ADD
? dynarray_count(point_layer->positions) - 1
- : (size_t) point_layer->selected;
+ : (size_t) point_layer->selection;
undo_context.type = type;
undo_context.layer = point_layer;
void point_layer_undo(void *context, size_t context_size)
{
trace_assert(context);
- trace_assert(sizeof(UndoContext) == context_size);
+ trace_assert(sizeof(PointUndoContext) == context_size);
- UndoContext *undo_context = context;
+ PointUndoContext *undo_context = context;
PointLayer *point_layer = undo_context->layer;
switch (undo_context->type) {
- case UNDO_ADD: {
+ case POINT_UNDO_ADD: {
dynarray_pop(point_layer->positions, NULL);
dynarray_pop(point_layer->colors, NULL);
dynarray_pop(point_layer->ids, NULL);
- point_layer->selected = -1;
+ point_layer->selection = -1;
} break;
- case UNDO_DELETE: {
+ 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->selected = -1;
+ point_layer->selection = -1;
} break;
- case UNDO_UPDATE: {
+ 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;
+
+ 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;
}
}
-#define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE) \
+#define POINT_UNDO_PUSH(HISTORY, CONTEXT) \
do { \
- UndoContext context = point_layer_create_undo_context(LAYER, UNDO_TYPE); \
+ PointUndoContext context = (CONTEXT); \
undo_history_push( \
HISTORY, \
point_layer_undo, \
return layer;
}
-PointLayer *create_point_layer(void)
+PointLayer *create_point_layer(const char *id_name_prefix)
{
Lt *lt = create_lt();
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);
}
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(
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(COLOR_RED);
}
static inline
-Triangle element_shape(Point position, float scale)
+Triangle element_shape(Vec2f position, float scale)
{
return triangle_mat3x3_product(
equilateral_triangle(),
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 Color color = color_scale(
- point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selected
+ 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));
- const Point position =
- point_layer->state == POINT_LAYER_MOVE && i == point_layer->selected
+ const Vec2f position =
+ point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
? point_layer->inter_position
: positions[i];
// Selection Layer
- if (active && i == point_layer->selected) {
+ if (active && i == point_layer->selection) {
if (camera_fill_triangle(
camera,
element_shape(
if (edit_field_render_world(
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);
+ 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;
}
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);
- UNDO_PUSH(point_layer, undo_history, UNDO_ADD);
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(point_layer, POINT_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);
+
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_swap_context(point_layer, a, b));
+}
+
static
void point_layer_delete_nth_element(PointLayer *point_layer,
size_t i,
{
trace_assert(point_layer);
- UNDO_PUSH(point_layer, undo_history, UNDO_DELETE);
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(
+ point_layer,
+ POINT_UNDO_DELETE));
dynarray_delete_at(point_layer->positions, i);
dynarray_delete_at(point_layer->colors, i);
}
if (selected) {
- if (point_layer->selected >= 0) {
+ if (point_layer->selection >= 0) {
point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
point_layer->state = POINT_LAYER_RECOLOR;
}
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,
undo_history);
} else {
Color *colors = dynarray_data(point_layer->colors);
- Point *positions = dynarray_data(point_layer->positions);
+ 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]);
- point_layer->inter_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) < 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)) {
+ 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->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) {
+ 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->selected >= 0) {
- clipboard = 1;
- dynarray_copy_to(point_layer->colors, &clipboard_color, (size_t)point_layer->selected);
+ 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) && clipboard) {
+ if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
int x, y;
SDL_GetMouseState(&x, &y);
- Point position = camera_map_screen(camera, x, y);
+ Vec2f position = camera_map_screen(camera, x, y);
point_layer_add_element(
point_layer,
position,
- clipboard_color,
+ point_clipboard_color,
undo_history);
}
} break;
case SDL_KEYDOWN: {
switch(event->key.keysym.sym) {
case SDLK_RETURN: {
- UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(
+ point_layer,
+ POINT_UNDO_UPDATE));
- char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
+ 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);
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: {
case SDL_BUTTON_LEFT: {
point_layer->state = POINT_LAYER_IDLE;
- // TODO(#1014): just click (without moving) on the point creates an undo history entry
- UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
-
- dynarray_replace_at(
- point_layer->positions,
- (size_t) point_layer->selected,
- &point_layer->inter_position);
+ 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_layer->inter_position =
- 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;
}
trace_assert(event);
trace_assert(camera);
trace_assert(undo_history);
- trace_assert(point_layer->selected >= 0);
+ trace_assert(point_layer->selection >= 0);
int selected = 0;
if (color_picker_event(
point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
if (!color_picker_drag(&point_layer->color_picker)) {
- UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
+ POINT_UNDO_PUSH(
+ undo_history,
+ create_point_undo_context(
+ point_layer,
+ POINT_UNDO_UPDATE));
dynarray_replace_at(
point_layer->colors,
- (size_t) point_layer->selected,
+ (size_t) point_layer->selection,
&point_layer->inter_color);
point_layer->state = POINT_LAYER_IDLE;
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);
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);