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 static int point_clipboard = 0;
25 static Color point_clipboard_color;
27 // TODO(#1140): PointLayer does not support snapping
39 PointLayerState state;
40 Dynarray/*<Vec2f>*/ *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 PointUndoContext create_point_undo_swap_context(PointLayer *point_layer,
73 size_t index, size_t index2)
75 trace_assert(point_layer);
76 trace_assert(index < dynarray_count(point_layer->positions));
77 trace_assert(index2 < dynarray_count(point_layer->positions));
79 PointUndoContext undo_context;
80 undo_context.type = POINT_UNDO_SWAP;
81 undo_context.layer = point_layer;
82 undo_context.index = index;
83 undo_context.index2 = index2;
88 PointUndoContext create_point_undo_context(PointLayer *point_layer,
91 trace_assert(type != POINT_UNDO_SWAP);
93 (void) create_point_undo_swap_context;
95 PointUndoContext undo_context;
98 type == POINT_UNDO_ADD
99 ? dynarray_count(point_layer->positions) - 1
100 : (size_t) point_layer->selection;
102 undo_context.type = type;
103 undo_context.layer = point_layer;
104 dynarray_copy_to(point_layer->positions, &undo_context.position, index);
105 dynarray_copy_to(point_layer->colors, &undo_context.color, index);
106 dynarray_copy_to(point_layer->ids, &undo_context.id, index);
107 undo_context.index = index;
113 void point_layer_undo(void *context, size_t context_size)
115 trace_assert(context);
116 trace_assert(sizeof(PointUndoContext) == context_size);
118 PointUndoContext *undo_context = context;
119 PointLayer *point_layer = undo_context->layer;
121 switch (undo_context->type) {
122 case POINT_UNDO_ADD: {
123 dynarray_pop(point_layer->positions, NULL);
124 dynarray_pop(point_layer->colors, NULL);
125 dynarray_pop(point_layer->ids, NULL);
126 point_layer->selection = -1;
129 case POINT_UNDO_DELETE: {
130 dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position);
131 dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color);
132 dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id);
133 point_layer->selection = -1;
136 case POINT_UNDO_UPDATE: {
137 dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position);
138 dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color);
139 dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id);
142 case POINT_UNDO_SWAP: {
143 dynarray_swap(point_layer->positions, undo_context->index, undo_context->index2);
144 dynarray_swap(point_layer->colors, undo_context->index, undo_context->index2);
145 dynarray_swap(point_layer->ids, undo_context->index, undo_context->index2);
150 #define POINT_UNDO_PUSH(HISTORY, CONTEXT) \
152 PointUndoContext context = (CONTEXT); \
160 LayerPtr point_layer_as_layer(PointLayer *point_layer)
169 PointLayer *create_point_layer(const char *id_name_prefix)
171 Lt *lt = create_lt();
173 PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
174 if (point_layer == NULL) {
177 point_layer->lt = lt;
179 point_layer->state = POINT_LAYER_IDLE;
181 point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Vec2f)), destroy_dynarray);
182 if (point_layer->positions == NULL) {
186 point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
187 if (point_layer->colors == NULL) {
191 point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
192 if (point_layer->ids == NULL) {
196 point_layer->edit_field = PUSH_LT(
199 POINT_LAYER_ID_TEXT_SIZE,
200 POINT_LAYER_ID_TEXT_COLOR),
202 if (point_layer->edit_field == NULL) {
206 point_layer->id_name_prefix = id_name_prefix;
211 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream,
212 const char *id_name_prefix)
214 trace_assert(line_stream);
216 PointLayer *point_layer = create_point_layer(id_name_prefix);
220 line_stream_next(line_stream),
223 log_fail("Could not read amount of points");
224 RETURN_LT(point_layer->lt, NULL);
228 char id[ID_MAX_SIZE];
230 for (size_t i = 0; i < count; ++i) {
232 line_stream_next(line_stream),
233 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
234 id, &x, &y, color_name) < 0) {
235 log_fail("Could not read %dth goal\n", i);
236 RETURN_LT(point_layer->lt, NULL);
238 const Color color = hexstr(color_name);
239 const Vec2f point = vec(x, y);
241 dynarray_push(point_layer->colors, &color);
242 dynarray_push(point_layer->positions, &point);
243 dynarray_push(point_layer->ids, id);
246 point_layer->selection = -1;
248 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
253 void destroy_point_layer(PointLayer *point_layer)
255 trace_assert(point_layer);
256 RETURN_LT0(point_layer->lt);
260 Triangle element_shape(Vec2f position, float scale)
262 return triangle_mat3x3_product(
263 equilateral_triangle(),
265 trans_mat_vec(position),
269 int point_layer_render(const PointLayer *point_layer,
270 const Camera *camera,
273 trace_assert(point_layer);
274 trace_assert(camera);
276 const int n = (int) dynarray_count(point_layer->positions);
277 Vec2f *positions = dynarray_data(point_layer->positions);
278 Color *colors = dynarray_data(point_layer->colors);
279 char *ids = dynarray_data(point_layer->ids);
281 for (int i = 0; i < n; ++i) {
282 const Color color = color_scale(
283 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
284 ? point_layer->inter_color
286 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
288 const Vec2f position =
289 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
290 ? point_layer->inter_position
294 if (active && i == point_layer->selection) {
295 if (camera_fill_triangle(
299 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
300 color_invert(color)) < 0) {
304 if (point_layer->state != POINT_LAYER_EDIT_ID &&
307 ids + ID_MAX_SIZE * i,
308 POINT_LAYER_ID_TEXT_SIZE,
309 POINT_LAYER_ID_TEXT_COLOR,
315 if (camera_fill_triangle(
319 POINT_LAYER_ELEMENT_RADIUS),
325 if (point_layer->state == POINT_LAYER_EDIT_ID) {
326 if (edit_field_render_world(
327 point_layer->edit_field,
329 positions[point_layer->selection]) < 0) {
334 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
343 int point_layer_element_at(const PointLayer *point_layer,
346 trace_assert(point_layer);
348 int n = (int) dynarray_count(point_layer->positions);
349 Vec2f *positions = dynarray_data(point_layer->positions);
351 for (int i = n - 1; i >= 0; --i) {
352 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
361 int point_layer_add_element(PointLayer *point_layer,
364 UndoHistory *undo_history)
366 trace_assert(point_layer);
367 trace_assert(undo_history);
369 char id[ID_MAX_SIZE];
370 snprintf(id, ID_MAX_SIZE, "%s_%d",
371 point_layer->id_name_prefix,
372 point_layer->id_name_counter++);
374 dynarray_push(point_layer->positions, &position);
375 dynarray_push(point_layer->colors, &color);
376 dynarray_push(point_layer->ids, id);
380 create_point_undo_context(point_layer, POINT_UNDO_ADD));
386 void point_layer_swap_elements(PointLayer *point_layer,
388 UndoHistory *undo_history)
390 trace_assert(point_layer);
391 trace_assert(undo_history);
392 trace_assert(a < dynarray_count(point_layer->positions));
393 trace_assert(b < dynarray_count(point_layer->positions));
395 dynarray_swap(point_layer->positions, a, b);
396 dynarray_swap(point_layer->colors, a, b);
397 dynarray_swap(point_layer->ids, a, b);
401 create_point_undo_swap_context(point_layer, a, b));
405 void point_layer_delete_nth_element(PointLayer *point_layer,
407 UndoHistory *undo_history)
409 trace_assert(point_layer);
413 create_point_undo_context(
417 dynarray_delete_at(point_layer->positions, i);
418 dynarray_delete_at(point_layer->colors, i);
419 dynarray_delete_at(point_layer->ids, i);
423 int point_layer_idle_event(PointLayer *point_layer,
424 const SDL_Event *event,
425 const Camera *camera,
426 UndoHistory *undo_history)
428 trace_assert(point_layer);
430 trace_assert(camera);
433 if (color_picker_event(
434 &point_layer->color_picker,
442 if (point_layer->selection >= 0) {
443 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
444 point_layer->state = POINT_LAYER_RECOLOR;
449 switch (event->type) {
450 case SDL_MOUSEBUTTONDOWN: {
451 switch (event->button.button) {
452 case SDL_BUTTON_LEFT: {
453 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
455 point_layer->selection = point_layer_element_at(
456 point_layer, position);
458 if (point_layer->selection < 0) {
459 point_layer_add_element(
462 color_picker_rgba(&point_layer->color_picker),
465 Color *colors = dynarray_data(point_layer->colors);
466 Vec2f *positions = dynarray_data(point_layer->positions);
468 point_layer->state = POINT_LAYER_MOVE;
469 point_layer->color_picker =
470 create_color_picker_from_rgba(colors[point_layer->selection]);
471 point_layer->inter_position = positions[point_layer->selection];
478 switch (event->key.keysym.sym) {
480 if ((event->key.keysym.mod & KMOD_SHIFT)
481 && (point_layer->selection >= 0)
482 && ((size_t)(point_layer->selection + 1) < dynarray_count(point_layer->positions))) {
483 point_layer_swap_elements(
485 (size_t) point_layer->selection,
486 (size_t) point_layer->selection + 1,
488 point_layer->selection++;
493 if ((event->key.keysym.mod & KMOD_SHIFT)
494 && (point_layer->selection > 0)
495 && ((size_t) point_layer->selection < dynarray_count(point_layer->positions))) {
496 point_layer_swap_elements(
498 (size_t) point_layer->selection,
499 (size_t) point_layer->selection - 1,
501 point_layer->selection--;
506 if (0 <= point_layer->selection && point_layer->selection < (int) dynarray_count(point_layer->positions)) {
507 point_layer_delete_nth_element(
509 (size_t)point_layer->selection,
511 point_layer->selection = -1;
516 if (point_layer->selection >= 0) {
517 char *ids = dynarray_data(point_layer->ids);
518 point_layer->state = POINT_LAYER_EDIT_ID;
520 point_layer->edit_field,
521 ids + ID_MAX_SIZE * point_layer->selection);
522 SDL_StartTextInput();
527 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
529 dynarray_copy_to(point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
534 if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
536 SDL_GetMouseState(&x, &y);
537 Vec2f position = camera_map_screen(camera, x, y);
539 point_layer_add_element(
542 point_clipboard_color,
554 int point_layer_edit_id_event(PointLayer *point_layer,
555 const SDL_Event *event,
556 const Camera *camera,
557 UndoHistory *undo_history)
559 trace_assert(point_layer);
561 trace_assert(camera);
563 switch (event->type) {
565 switch(event->key.keysym.sym) {
569 create_point_undo_context(
573 char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selection);
574 const char *text = edit_field_as_text(point_layer->edit_field);
575 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
577 memset(id + n, 0, ID_MAX_SIZE - n);
579 point_layer->state = POINT_LAYER_IDLE;
585 point_layer->state = POINT_LAYER_IDLE;
593 return edit_field_event(point_layer->edit_field, event);
597 int point_layer_move_event(PointLayer *point_layer,
598 const SDL_Event *event,
599 const Camera *camera,
600 UndoHistory *undo_history)
602 trace_assert(point_layer);
604 trace_assert(camera);
605 trace_assert(point_layer->selection >= 0);
607 Vec2f *positions = dynarray_data(point_layer->positions);
609 switch (event->type) {
610 case SDL_MOUSEBUTTONUP: {
611 switch (event->button.button) {
612 case SDL_BUTTON_LEFT: {
613 point_layer->state = POINT_LAYER_IDLE;
615 const float distance = vec_length(
616 vec_sub(point_layer->inter_position,
617 positions[point_layer->selection]));
619 if (distance > 1e-6) {
622 create_point_undo_context(
627 point_layer->positions,
628 (size_t) point_layer->selection,
629 &point_layer->inter_position);
635 case SDL_MOUSEMOTION: {
636 const Uint8 *state = SDL_GetKeyboardState(NULL);
637 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
638 const Vec2f point_pos = positions[point_layer->selection];
640 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
641 point_layer->inter_position = mouse_pos;
643 const float dx = fabsf(point_pos.x - mouse_pos.x);
644 const float dy = fabsf(point_pos.y - mouse_pos.y);
647 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
649 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
659 int point_layer_recolor_event(PointLayer *point_layer,
660 const SDL_Event *event,
661 const Camera *camera,
662 UndoHistory *undo_history)
664 trace_assert(point_layer);
666 trace_assert(camera);
667 trace_assert(undo_history);
668 trace_assert(point_layer->selection >= 0);
671 if (color_picker_event(
672 &point_layer->color_picker,
680 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
682 if (!color_picker_drag(&point_layer->color_picker)) {
685 create_point_undo_context(
691 (size_t) point_layer->selection,
692 &point_layer->inter_color);
694 point_layer->state = POINT_LAYER_IDLE;
702 int point_layer_event(PointLayer *point_layer,
703 const SDL_Event *event,
704 const Camera *camera,
705 UndoHistory *undo_history)
707 trace_assert(point_layer);
709 trace_assert(camera);
710 trace_assert(undo_history);
712 switch (point_layer->state) {
713 case POINT_LAYER_IDLE:
714 return point_layer_idle_event(point_layer, event, camera, undo_history);
716 case POINT_LAYER_EDIT_ID:
717 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
719 case POINT_LAYER_MOVE:
720 return point_layer_move_event(point_layer, event, camera, undo_history);
722 case POINT_LAYER_RECOLOR:
723 return point_layer_recolor_event(point_layer, event, camera, undo_history);
729 size_t point_layer_count(const PointLayer *point_layer)
731 trace_assert(point_layer);
732 return dynarray_count(point_layer->positions);
735 const Vec2f *point_layer_positions(const PointLayer *point_layer)
737 trace_assert(point_layer);
738 return dynarray_data(point_layer->positions);
741 const Color *point_layer_colors(const PointLayer *point_layer)
743 trace_assert(point_layer);
744 return dynarray_data(point_layer->colors);
747 const char *point_layer_ids(const PointLayer *point_layer)
749 trace_assert(point_layer);
750 return dynarray_data(point_layer->ids);
753 int point_layer_dump_stream(const PointLayer *point_layer,
756 trace_assert(point_layer);
757 trace_assert(filedump);
759 size_t n = dynarray_count(point_layer->ids);
760 char *ids = dynarray_data(point_layer->ids);
761 Vec2f *positions = dynarray_data(point_layer->positions);
762 Color *colors = dynarray_data(point_layer->colors);
764 fprintf(filedump, "%zd\n", n);
765 for (size_t i = 0; i < n; ++i) {
766 fprintf(filedump, "%s %f %f ",
767 ids + ID_MAX_SIZE * i,
768 positions[i].x, positions[i].y);
769 color_hex_to_stream(colors[i], filedump);
770 fprintf(filedump, "\n");