6 #include "game/camera.h"
7 #include "system/line_stream.h"
8 #include "system/log.h"
10 #include "system/nth_alloc.h"
11 #include "system/stacktrace.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14 #include "./point_layer.h"
15 #include "math/extrema.h"
16 #include "math/mat3x3.h"
17 #include "./color_picker.h"
18 #include "undo_history.h"
20 #define POINT_LAYER_ELEMENT_RADIUS 10.0f
21 #define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
22 #define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
24 // TODO: PointLayer does not support z reordering
26 static int clipboard = 0;
27 static Color clipboard_color;
39 PointLayerState state;
40 Dynarray/*<Point>*/ *positions;
41 Dynarray/*<Color>*/ *colors;
42 Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
44 ColorPicker color_picker;
48 Edit_field *edit_field;
51 const char *id_name_prefix;
72 UndoContext create_undo_swap_context(PointLayer *point_layer,
73 size_t index, size_t index2)
75 UndoContext undo_context;
76 undo_context.type = UNDO_SWAP;
77 undo_context.layer = point_layer;
78 undo_context.index = index;
79 undo_context.index2 = index2;
84 UndoContext create_undo_context(PointLayer *point_layer,
87 trace_assert(type != UNDO_SWAP);
89 (void) create_undo_swap_context;
91 UndoContext undo_context;
95 ? dynarray_count(point_layer->positions) - 1
96 : (size_t) point_layer->selected;
98 undo_context.type = type;
99 undo_context.layer = point_layer;
100 dynarray_copy_to(point_layer->positions, &undo_context.position, index);
101 dynarray_copy_to(point_layer->colors, &undo_context.color, index);
102 dynarray_copy_to(point_layer->ids, &undo_context.id, index);
103 undo_context.index = index;
109 void point_layer_undo(void *context, size_t context_size)
111 trace_assert(context);
112 trace_assert(sizeof(UndoContext) == context_size);
114 UndoContext *undo_context = context;
115 PointLayer *point_layer = undo_context->layer;
117 switch (undo_context->type) {
119 dynarray_pop(point_layer->positions, NULL);
120 dynarray_pop(point_layer->colors, NULL);
121 dynarray_pop(point_layer->ids, NULL);
122 point_layer->selected = -1;
126 dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position);
127 dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color);
128 dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id);
129 point_layer->selected = -1;
133 dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position);
134 dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color);
135 dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id);
139 dynarray_swap(point_layer->positions, undo_context->index, undo_context->index2);
140 dynarray_swap(point_layer->colors, undo_context->index, undo_context->index2);
141 dynarray_swap(point_layer->ids, undo_context->index, undo_context->index2);
146 #define UNDO_PUSH(HISTORY, CONTEXT) \
148 UndoContext context = (CONTEXT); \
156 LayerPtr point_layer_as_layer(PointLayer *point_layer)
165 PointLayer *create_point_layer(const char *id_name_prefix)
167 Lt *lt = create_lt();
169 PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
170 if (point_layer == NULL) {
173 point_layer->lt = lt;
175 point_layer->state = POINT_LAYER_IDLE;
177 point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
178 if (point_layer->positions == NULL) {
182 point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
183 if (point_layer->colors == NULL) {
187 point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
188 if (point_layer->ids == NULL) {
192 point_layer->edit_field = PUSH_LT(
195 POINT_LAYER_ID_TEXT_SIZE,
196 POINT_LAYER_ID_TEXT_COLOR),
198 if (point_layer->edit_field == NULL) {
202 point_layer->id_name_prefix = id_name_prefix;
207 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream,
208 const char *id_name_prefix)
210 trace_assert(line_stream);
212 PointLayer *point_layer = create_point_layer(id_name_prefix);
216 line_stream_next(line_stream),
219 log_fail("Could not read amount of points");
220 RETURN_LT(point_layer->lt, NULL);
224 char id[ID_MAX_SIZE];
226 for (size_t i = 0; i < count; ++i) {
228 line_stream_next(line_stream),
229 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
230 id, &x, &y, color_name) < 0) {
231 log_fail("Could not read %dth goal\n", i);
232 RETURN_LT(point_layer->lt, NULL);
234 const Color color = hexstr(color_name);
235 const Point point = vec(x, y);
237 dynarray_push(point_layer->colors, &color);
238 dynarray_push(point_layer->positions, &point);
239 dynarray_push(point_layer->ids, id);
242 point_layer->selected = -1;
244 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
249 void destroy_point_layer(PointLayer *point_layer)
251 trace_assert(point_layer);
252 RETURN_LT0(point_layer->lt);
256 Triangle element_shape(Point position, float scale)
258 return triangle_mat3x3_product(
259 equilateral_triangle(),
261 trans_mat_vec(position),
265 int point_layer_render(const PointLayer *point_layer,
266 const Camera *camera,
269 trace_assert(point_layer);
270 trace_assert(camera);
272 const int n = (int) dynarray_count(point_layer->positions);
273 Point *positions = dynarray_data(point_layer->positions);
274 Color *colors = dynarray_data(point_layer->colors);
275 char *ids = dynarray_data(point_layer->ids);
277 for (int i = 0; i < n; ++i) {
278 const Color color = color_scale(
279 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selected
280 ? point_layer->inter_color
282 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
284 const Point position =
285 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selected
286 ? point_layer->inter_position
290 if (active && i == point_layer->selected) {
291 if (camera_fill_triangle(
295 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
296 color_invert(color)) < 0) {
300 if (point_layer->state != POINT_LAYER_EDIT_ID &&
303 ids + ID_MAX_SIZE * i,
304 POINT_LAYER_ID_TEXT_SIZE,
305 POINT_LAYER_ID_TEXT_COLOR,
311 if (camera_fill_triangle(
315 POINT_LAYER_ELEMENT_RADIUS),
321 if (point_layer->state == POINT_LAYER_EDIT_ID) {
322 if (edit_field_render_world(
323 point_layer->edit_field,
325 positions[point_layer->selected]) < 0) {
330 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
339 int point_layer_element_at(const PointLayer *point_layer,
342 trace_assert(point_layer);
344 int n = (int) dynarray_count(point_layer->positions);
345 Point *positions = dynarray_data(point_layer->positions);
347 for (int i = n - 1; i >= 0; --i) {
348 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
357 int point_layer_add_element(PointLayer *point_layer,
360 UndoHistory *undo_history)
362 trace_assert(point_layer);
363 trace_assert(undo_history);
365 char id[ID_MAX_SIZE];
366 snprintf(id, ID_MAX_SIZE, "%s_%d",
367 point_layer->id_name_prefix,
368 point_layer->id_name_counter++);
370 dynarray_push(point_layer->positions, &position);
371 dynarray_push(point_layer->colors, &color);
372 dynarray_push(point_layer->ids, id);
376 create_undo_context(point_layer, UNDO_ADD));
382 void point_layer_delete_nth_element(PointLayer *point_layer,
384 UndoHistory *undo_history)
386 trace_assert(point_layer);
394 dynarray_delete_at(point_layer->positions, i);
395 dynarray_delete_at(point_layer->colors, i);
396 dynarray_delete_at(point_layer->ids, i);
400 int point_layer_idle_event(PointLayer *point_layer,
401 const SDL_Event *event,
402 const Camera *camera,
403 UndoHistory *undo_history)
405 trace_assert(point_layer);
407 trace_assert(camera);
410 if (color_picker_event(
411 &point_layer->color_picker,
419 if (point_layer->selected >= 0) {
420 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
421 point_layer->state = POINT_LAYER_RECOLOR;
426 switch (event->type) {
427 case SDL_MOUSEBUTTONDOWN: {
428 switch (event->button.button) {
429 case SDL_BUTTON_LEFT: {
430 const Point position = camera_map_screen(camera, event->button.x, event->button.y);
432 point_layer->selected = point_layer_element_at(
433 point_layer, position);
435 if (point_layer->selected < 0) {
436 point_layer_add_element(
439 color_picker_rgba(&point_layer->color_picker),
442 Color *colors = dynarray_data(point_layer->colors);
443 Point *positions = dynarray_data(point_layer->positions);
445 point_layer->state = POINT_LAYER_MOVE;
446 point_layer->color_picker =
447 create_color_picker_from_rgba(colors[point_layer->selected]);
448 point_layer->inter_position = positions[point_layer->selected];
455 switch (event->key.keysym.sym) {
457 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
458 point_layer_delete_nth_element(
460 (size_t)point_layer->selected,
462 point_layer->selected = -1;
467 if (point_layer->selected >= 0) {
468 char *ids = dynarray_data(point_layer->ids);
469 point_layer->state = POINT_LAYER_EDIT_ID;
471 point_layer->edit_field,
472 ids + ID_MAX_SIZE * point_layer->selected);
473 SDL_StartTextInput();
478 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selected >= 0) {
480 dynarray_copy_to(point_layer->colors, &clipboard_color, (size_t)point_layer->selected);
485 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
487 SDL_GetMouseState(&x, &y);
488 Point position = camera_map_screen(camera, x, y);
490 point_layer_add_element(
505 int point_layer_edit_id_event(PointLayer *point_layer,
506 const SDL_Event *event,
507 const Camera *camera,
508 UndoHistory *undo_history)
510 trace_assert(point_layer);
512 trace_assert(camera);
514 switch (event->type) {
516 switch(event->key.keysym.sym) {
524 char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
525 const char *text = edit_field_as_text(point_layer->edit_field);
526 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
528 memset(id + n, 0, ID_MAX_SIZE - n);
530 point_layer->state = POINT_LAYER_IDLE;
536 point_layer->state = POINT_LAYER_IDLE;
544 return edit_field_event(point_layer->edit_field, event);
548 int point_layer_move_event(PointLayer *point_layer,
549 const SDL_Event *event,
550 const Camera *camera,
551 UndoHistory *undo_history)
553 trace_assert(point_layer);
555 trace_assert(camera);
556 trace_assert(point_layer->selected >= 0);
558 switch (event->type) {
559 case SDL_MOUSEBUTTONUP: {
560 switch (event->button.button) {
561 case SDL_BUTTON_LEFT: {
562 point_layer->state = POINT_LAYER_IDLE;
564 // TODO(#1014): just click (without moving) on the point creates an undo history entry
572 point_layer->positions,
573 (size_t) point_layer->selected,
574 &point_layer->inter_position);
579 case SDL_MOUSEMOTION: {
580 point_layer->inter_position =
581 camera_map_screen(camera, event->motion.x, event->motion.y);
589 int point_layer_recolor_event(PointLayer *point_layer,
590 const SDL_Event *event,
591 const Camera *camera,
592 UndoHistory *undo_history)
594 trace_assert(point_layer);
596 trace_assert(camera);
597 trace_assert(undo_history);
598 trace_assert(point_layer->selected >= 0);
601 if (color_picker_event(
602 &point_layer->color_picker,
610 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
612 if (!color_picker_drag(&point_layer->color_picker)) {
621 (size_t) point_layer->selected,
622 &point_layer->inter_color);
624 point_layer->state = POINT_LAYER_IDLE;
632 int point_layer_event(PointLayer *point_layer,
633 const SDL_Event *event,
634 const Camera *camera,
635 UndoHistory *undo_history)
637 trace_assert(point_layer);
639 trace_assert(camera);
640 trace_assert(undo_history);
642 switch (point_layer->state) {
643 case POINT_LAYER_IDLE:
644 return point_layer_idle_event(point_layer, event, camera, undo_history);
646 case POINT_LAYER_EDIT_ID:
647 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
649 case POINT_LAYER_MOVE:
650 return point_layer_move_event(point_layer, event, camera, undo_history);
652 case POINT_LAYER_RECOLOR:
653 return point_layer_recolor_event(point_layer, event, camera, undo_history);
659 size_t point_layer_count(const PointLayer *point_layer)
661 trace_assert(point_layer);
662 return dynarray_count(point_layer->positions);
665 const Point *point_layer_positions(const PointLayer *point_layer)
667 trace_assert(point_layer);
668 return dynarray_data(point_layer->positions);
671 const Color *point_layer_colors(const PointLayer *point_layer)
673 trace_assert(point_layer);
674 return dynarray_data(point_layer->colors);
677 const char *point_layer_ids(const PointLayer *point_layer)
679 trace_assert(point_layer);
680 return dynarray_data(point_layer->ids);
683 int point_layer_dump_stream(const PointLayer *point_layer,
686 trace_assert(point_layer);
687 trace_assert(filedump);
689 size_t n = dynarray_count(point_layer->ids);
690 char *ids = dynarray_data(point_layer->ids);
691 Point *positions = dynarray_data(point_layer->positions);
692 Color *colors = dynarray_data(point_layer->colors);
694 fprintf(filedump, "%zd\n", n);
695 for (size_t i = 0; i < n; ++i) {
696 fprintf(filedump, "%s %f %f ",
697 ids + ID_MAX_SIZE * i,
698 positions[i].x, positions[i].y);
699 color_hex_to_stream(colors[i], filedump);
700 fprintf(filedump, "\n");