+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)
+