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 < point_layer->positions.count);
77 trace_assert(index2 < point_layer->positions.count);
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 ? point_layer->positions.count - 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 = create_dynarray(sizeof(Vec2f));
182 point_layer->colors = create_dynarray(sizeof(Color));
183 point_layer->ids = create_dynarray(sizeof(char) * ID_MAX_SIZE);
185 point_layer->edit_field = PUSH_LT(
188 POINT_LAYER_ID_TEXT_SIZE,
189 POINT_LAYER_ID_TEXT_COLOR),
191 if (point_layer->edit_field == NULL) {
195 point_layer->id_name_prefix = id_name_prefix;
200 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream,
201 const char *id_name_prefix)
203 trace_assert(line_stream);
205 PointLayer *point_layer = create_point_layer(id_name_prefix);
209 line_stream_next(line_stream),
212 log_fail("Could not read amount of points");
213 RETURN_LT(point_layer->lt, NULL);
217 char id[ID_MAX_SIZE];
219 for (size_t i = 0; i < count; ++i) {
221 line_stream_next(line_stream),
222 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
223 id, &x, &y, color_name) < 0) {
224 log_fail("Could not read %dth goal\n", i);
225 RETURN_LT(point_layer->lt, NULL);
227 const Color color = hexstr(color_name);
228 const Vec2f point = vec(x, y);
230 dynarray_push(&point_layer->colors, &color);
231 dynarray_push(&point_layer->positions, &point);
232 dynarray_push(&point_layer->ids, id);
235 point_layer->selection = -1;
237 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
242 PointLayer *chop_point_layer(Memory *memory,
244 const char *id_name_prefix)
246 trace_assert(memory);
248 trace_assert(id_name_prefix);
250 PointLayer *point_layer = create_point_layer(id_name_prefix);
252 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
253 char id[ENTITY_MAX_ID_SIZE];
254 for (int i = 0; i < n; ++i) {
255 String line = trim(chop_by_delim(input, '\n'));
256 String string_id = trim(chop_word(&line));
258 point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
259 point.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
260 Color color = hexs(trim(chop_word(&line)));
262 memset(id, 0, ENTITY_MAX_ID_SIZE);
266 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
268 dynarray_push(&point_layer->positions, &point);
269 dynarray_push(&point_layer->colors, &color);
270 dynarray_push(&point_layer->ids, id);
273 point_layer->selection = -1;
274 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
279 void destroy_point_layer(PointLayer *point_layer)
281 trace_assert(point_layer);
283 free(point_layer->positions.data);
284 free(point_layer->colors.data);
285 free(point_layer->ids.data);
287 RETURN_LT0(point_layer->lt);
291 Triangle element_shape(Vec2f position, float scale)
293 return triangle_mat3x3_product(
294 equilateral_triangle(),
296 trans_mat_vec(position),
300 int point_layer_render(const PointLayer *point_layer,
301 const Camera *camera,
304 trace_assert(point_layer);
305 trace_assert(camera);
307 const int n = (int)point_layer->positions.count;
308 Vec2f *positions = (Vec2f *)point_layer->positions.data;
309 Color *colors = (Color *)point_layer->colors.data;
310 char *ids = (char *)point_layer->ids.data;
312 for (int i = 0; i < n; ++i) {
313 const Color color = color_scale(
314 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
315 ? point_layer->inter_color
317 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
319 const Vec2f position =
320 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
321 ? point_layer->inter_position
325 if (active && i == point_layer->selection) {
326 if (camera_fill_triangle(
330 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
331 color_invert(color)) < 0) {
335 if (point_layer->state != POINT_LAYER_EDIT_ID &&
338 ids + ID_MAX_SIZE * i,
339 POINT_LAYER_ID_TEXT_SIZE,
340 POINT_LAYER_ID_TEXT_COLOR,
346 if (camera_fill_triangle(
350 POINT_LAYER_ELEMENT_RADIUS),
356 if (point_layer->state == POINT_LAYER_EDIT_ID) {
357 if (edit_field_render_world(
358 point_layer->edit_field,
360 positions[point_layer->selection]) < 0) {
365 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
374 int point_layer_element_at(const PointLayer *point_layer,
377 trace_assert(point_layer);
379 int n = (int) point_layer->positions.count;
380 Vec2f *positions = (Vec2f *)point_layer->positions.data;
382 for (int i = n - 1; i >= 0; --i) {
383 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
392 int point_layer_add_element(PointLayer *point_layer,
395 UndoHistory *undo_history)
397 trace_assert(point_layer);
398 trace_assert(undo_history);
400 char id[ID_MAX_SIZE];
401 snprintf(id, ID_MAX_SIZE, "%s_%d",
402 point_layer->id_name_prefix,
403 point_layer->id_name_counter++);
405 dynarray_push(&point_layer->positions, &position);
406 dynarray_push(&point_layer->colors, &color);
407 dynarray_push(&point_layer->ids, id);
411 create_point_undo_context(point_layer, POINT_UNDO_ADD));
417 void point_layer_swap_elements(PointLayer *point_layer,
419 UndoHistory *undo_history)
421 trace_assert(point_layer);
422 trace_assert(undo_history);
423 trace_assert(a < point_layer->positions.count);
424 trace_assert(b < point_layer->positions.count);
426 dynarray_swap(&point_layer->positions, a, b);
427 dynarray_swap(&point_layer->colors, a, b);
428 dynarray_swap(&point_layer->ids, a, b);
432 create_point_undo_swap_context(point_layer, a, b));
436 void point_layer_delete_nth_element(PointLayer *point_layer,
438 UndoHistory *undo_history)
440 trace_assert(point_layer);
444 create_point_undo_context(
448 dynarray_delete_at(&point_layer->positions, i);
449 dynarray_delete_at(&point_layer->colors, i);
450 dynarray_delete_at(&point_layer->ids, i);
454 int point_layer_idle_event(PointLayer *point_layer,
455 const SDL_Event *event,
456 const Camera *camera,
457 UndoHistory *undo_history)
459 trace_assert(point_layer);
461 trace_assert(camera);
464 if (color_picker_event(
465 &point_layer->color_picker,
473 if (point_layer->selection >= 0) {
474 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
475 point_layer->state = POINT_LAYER_RECOLOR;
480 switch (event->type) {
481 case SDL_MOUSEBUTTONDOWN: {
482 switch (event->button.button) {
483 case SDL_BUTTON_LEFT: {
484 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
486 point_layer->selection = point_layer_element_at(
487 point_layer, position);
489 if (point_layer->selection < 0) {
490 point_layer_add_element(
493 color_picker_rgba(&point_layer->color_picker),
496 Color *colors = (Color*)point_layer->colors.data;
497 Vec2f *positions = (Vec2f*)point_layer->positions.data;
499 point_layer->state = POINT_LAYER_MOVE;
500 point_layer->color_picker =
501 create_color_picker_from_rgba(colors[point_layer->selection]);
502 point_layer->inter_position = positions[point_layer->selection];
509 switch (event->key.keysym.sym) {
511 if ((event->key.keysym.mod & KMOD_SHIFT)
512 && (point_layer->selection >= 0)
513 && ((size_t)(point_layer->selection + 1) < point_layer->positions.count)) {
514 point_layer_swap_elements(
516 (size_t) point_layer->selection,
517 (size_t) point_layer->selection + 1,
519 point_layer->selection++;
524 if ((event->key.keysym.mod & KMOD_SHIFT)
525 && (point_layer->selection > 0)
526 && ((size_t) point_layer->selection < point_layer->positions.count)) {
527 point_layer_swap_elements(
529 (size_t) point_layer->selection,
530 (size_t) point_layer->selection - 1,
532 point_layer->selection--;
537 if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
538 point_layer_delete_nth_element(
540 (size_t)point_layer->selection,
542 point_layer->selection = -1;
547 if (point_layer->selection >= 0) {
548 char *ids = (char*)point_layer->ids.data;
549 point_layer->state = POINT_LAYER_EDIT_ID;
551 point_layer->edit_field,
552 ids + ID_MAX_SIZE * point_layer->selection);
553 SDL_StartTextInput();
558 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
560 dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
565 if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
567 SDL_GetMouseState(&x, &y);
568 Vec2f position = camera_map_screen(camera, x, y);
570 point_layer_add_element(
573 point_clipboard_color,
585 int point_layer_edit_id_event(PointLayer *point_layer,
586 const SDL_Event *event,
587 const Camera *camera,
588 UndoHistory *undo_history)
590 trace_assert(point_layer);
592 trace_assert(camera);
594 switch (event->type) {
596 switch(event->key.keysym.sym) {
600 create_point_undo_context(
604 char *id = dynarray_pointer_at(&point_layer->ids, (size_t) point_layer->selection);
605 const char *text = edit_field_as_text(point_layer->edit_field);
606 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
608 memset(id + n, 0, ID_MAX_SIZE - n);
610 point_layer->state = POINT_LAYER_IDLE;
616 point_layer->state = POINT_LAYER_IDLE;
624 return edit_field_event(point_layer->edit_field, event);
628 int point_layer_move_event(PointLayer *point_layer,
629 const SDL_Event *event,
630 const Camera *camera,
631 UndoHistory *undo_history)
633 trace_assert(point_layer);
635 trace_assert(camera);
636 trace_assert(point_layer->selection >= 0);
638 Vec2f *positions = (Vec2f*)point_layer->positions.data;
640 switch (event->type) {
641 case SDL_MOUSEBUTTONUP: {
642 switch (event->button.button) {
643 case SDL_BUTTON_LEFT: {
644 point_layer->state = POINT_LAYER_IDLE;
646 const float distance = vec_length(
647 vec_sub(point_layer->inter_position,
648 positions[point_layer->selection]));
650 if (distance > 1e-6) {
653 create_point_undo_context(
658 &point_layer->positions,
659 (size_t) point_layer->selection,
660 &point_layer->inter_position);
666 case SDL_MOUSEMOTION: {
667 const Uint8 *state = SDL_GetKeyboardState(NULL);
668 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
669 const Vec2f point_pos = positions[point_layer->selection];
671 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
672 point_layer->inter_position = mouse_pos;
674 const float dx = fabsf(point_pos.x - mouse_pos.x);
675 const float dy = fabsf(point_pos.y - mouse_pos.y);
678 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
680 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
690 int point_layer_recolor_event(PointLayer *point_layer,
691 const SDL_Event *event,
692 const Camera *camera,
693 UndoHistory *undo_history)
695 trace_assert(point_layer);
697 trace_assert(camera);
698 trace_assert(undo_history);
699 trace_assert(point_layer->selection >= 0);
702 if (color_picker_event(
703 &point_layer->color_picker,
711 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
713 if (!color_picker_drag(&point_layer->color_picker)) {
716 create_point_undo_context(
721 &point_layer->colors,
722 (size_t) point_layer->selection,
723 &point_layer->inter_color);
725 point_layer->state = POINT_LAYER_IDLE;
733 int point_layer_event(PointLayer *point_layer,
734 const SDL_Event *event,
735 const Camera *camera,
736 UndoHistory *undo_history)
738 trace_assert(point_layer);
740 trace_assert(camera);
741 trace_assert(undo_history);
743 switch (point_layer->state) {
744 case POINT_LAYER_IDLE:
745 return point_layer_idle_event(point_layer, event, camera, undo_history);
747 case POINT_LAYER_EDIT_ID:
748 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
750 case POINT_LAYER_MOVE:
751 return point_layer_move_event(point_layer, event, camera, undo_history);
753 case POINT_LAYER_RECOLOR:
754 return point_layer_recolor_event(point_layer, event, camera, undo_history);
760 size_t point_layer_count(const PointLayer *point_layer)
762 trace_assert(point_layer);
763 return point_layer->positions.count;
766 const Vec2f *point_layer_positions(const PointLayer *point_layer)
768 trace_assert(point_layer);
769 return (const Vec2f *)point_layer->positions.data;
772 const Color *point_layer_colors(const PointLayer *point_layer)
774 trace_assert(point_layer);
775 return (const Color *)point_layer->colors.data;
778 const char *point_layer_ids(const PointLayer *point_layer)
780 trace_assert(point_layer);
781 return (const char *)point_layer->ids.data;
784 int point_layer_dump_stream(const PointLayer *point_layer,
787 trace_assert(point_layer);
788 trace_assert(filedump);
790 size_t n = point_layer->ids.count;
791 char *ids = (char *) point_layer->ids.data;
792 Vec2f *positions = (Vec2f *) point_layer->positions.data;
793 Color *colors = (Color *) point_layer->colors.data;
795 fprintf(filedump, "%zd\n", n);
796 for (size_t i = 0; i < n; ++i) {
797 fprintf(filedump, "%s %f %f ",
798 ids + ID_MAX_SIZE * i,
799 positions[i].x, positions[i].y);
800 color_hex_to_stream(colors[i], filedump);
801 fprintf(filedump, "\n");