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 clipboard = 0;
25 static Color clipboard_color;
37 PointLayerState state;
38 Dynarray/*<Vec2f>*/ *positions;
39 Dynarray/*<Color>*/ *colors;
40 Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
42 ColorPicker color_picker;
46 Edit_field *edit_field;
49 const char *id_name_prefix;
70 UndoContext create_undo_swap_context(PointLayer *point_layer,
71 size_t index, size_t index2)
73 trace_assert(point_layer);
74 trace_assert(index < dynarray_count(point_layer->positions));
75 trace_assert(index2 < dynarray_count(point_layer->positions));
77 UndoContext undo_context;
78 undo_context.type = UNDO_SWAP;
79 undo_context.layer = point_layer;
80 undo_context.index = index;
81 undo_context.index2 = index2;
86 UndoContext create_undo_context(PointLayer *point_layer,
89 trace_assert(type != UNDO_SWAP);
91 (void) create_undo_swap_context;
93 UndoContext undo_context;
97 ? dynarray_count(point_layer->positions) - 1
98 : (size_t) point_layer->selection;
100 undo_context.type = type;
101 undo_context.layer = point_layer;
102 dynarray_copy_to(point_layer->positions, &undo_context.position, index);
103 dynarray_copy_to(point_layer->colors, &undo_context.color, index);
104 dynarray_copy_to(point_layer->ids, &undo_context.id, index);
105 undo_context.index = index;
111 void point_layer_undo(void *context, size_t context_size)
113 trace_assert(context);
114 trace_assert(sizeof(UndoContext) == context_size);
116 UndoContext *undo_context = context;
117 PointLayer *point_layer = undo_context->layer;
119 switch (undo_context->type) {
121 dynarray_pop(point_layer->positions, NULL);
122 dynarray_pop(point_layer->colors, NULL);
123 dynarray_pop(point_layer->ids, NULL);
124 point_layer->selection = -1;
128 dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position);
129 dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color);
130 dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id);
131 point_layer->selection = -1;
135 dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position);
136 dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color);
137 dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id);
141 dynarray_swap(point_layer->positions, undo_context->index, undo_context->index2);
142 dynarray_swap(point_layer->colors, undo_context->index, undo_context->index2);
143 dynarray_swap(point_layer->ids, undo_context->index, undo_context->index2);
148 #define UNDO_PUSH(HISTORY, CONTEXT) \
150 UndoContext context = (CONTEXT); \
158 LayerPtr point_layer_as_layer(PointLayer *point_layer)
167 PointLayer *create_point_layer(const char *id_name_prefix)
169 Lt *lt = create_lt();
171 PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
172 if (point_layer == NULL) {
175 point_layer->lt = lt;
177 point_layer->state = POINT_LAYER_IDLE;
179 point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Vec2f)), destroy_dynarray);
180 if (point_layer->positions == NULL) {
184 point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
185 if (point_layer->colors == NULL) {
189 point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
190 if (point_layer->ids == NULL) {
194 point_layer->edit_field = PUSH_LT(
197 POINT_LAYER_ID_TEXT_SIZE,
198 POINT_LAYER_ID_TEXT_COLOR),
200 if (point_layer->edit_field == NULL) {
204 point_layer->id_name_prefix = id_name_prefix;
209 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream,
210 const char *id_name_prefix)
212 trace_assert(line_stream);
214 PointLayer *point_layer = create_point_layer(id_name_prefix);
218 line_stream_next(line_stream),
221 log_fail("Could not read amount of points");
222 RETURN_LT(point_layer->lt, NULL);
226 char id[ID_MAX_SIZE];
228 for (size_t i = 0; i < count; ++i) {
230 line_stream_next(line_stream),
231 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
232 id, &x, &y, color_name) < 0) {
233 log_fail("Could not read %dth goal\n", i);
234 RETURN_LT(point_layer->lt, NULL);
236 const Color color = hexstr(color_name);
237 const Vec2f point = vec(x, y);
239 dynarray_push(point_layer->colors, &color);
240 dynarray_push(point_layer->positions, &point);
241 dynarray_push(point_layer->ids, id);
244 point_layer->selection = -1;
246 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
251 void destroy_point_layer(PointLayer *point_layer)
253 trace_assert(point_layer);
254 RETURN_LT0(point_layer->lt);
258 Triangle element_shape(Vec2f position, float scale)
260 return triangle_mat3x3_product(
261 equilateral_triangle(),
263 trans_mat_vec(position),
267 int point_layer_render(const PointLayer *point_layer,
268 const Camera *camera,
271 trace_assert(point_layer);
272 trace_assert(camera);
274 const int n = (int) dynarray_count(point_layer->positions);
275 Vec2f *positions = dynarray_data(point_layer->positions);
276 Color *colors = dynarray_data(point_layer->colors);
277 char *ids = dynarray_data(point_layer->ids);
279 for (int i = 0; i < n; ++i) {
280 const Color color = color_scale(
281 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
282 ? point_layer->inter_color
284 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
286 const Vec2f position =
287 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
288 ? point_layer->inter_position
292 if (active && i == point_layer->selection) {
293 if (camera_fill_triangle(
297 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
298 color_invert(color)) < 0) {
302 if (point_layer->state != POINT_LAYER_EDIT_ID &&
305 ids + ID_MAX_SIZE * i,
306 POINT_LAYER_ID_TEXT_SIZE,
307 POINT_LAYER_ID_TEXT_COLOR,
313 if (camera_fill_triangle(
317 POINT_LAYER_ELEMENT_RADIUS),
323 if (point_layer->state == POINT_LAYER_EDIT_ID) {
324 if (edit_field_render_world(
325 point_layer->edit_field,
327 positions[point_layer->selection]) < 0) {
332 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
341 int point_layer_element_at(const PointLayer *point_layer,
344 trace_assert(point_layer);
346 int n = (int) dynarray_count(point_layer->positions);
347 Vec2f *positions = dynarray_data(point_layer->positions);
349 for (int i = n - 1; i >= 0; --i) {
350 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
359 int point_layer_add_element(PointLayer *point_layer,
362 UndoHistory *undo_history)
364 trace_assert(point_layer);
365 trace_assert(undo_history);
367 char id[ID_MAX_SIZE];
368 snprintf(id, ID_MAX_SIZE, "%s_%d",
369 point_layer->id_name_prefix,
370 point_layer->id_name_counter++);
372 dynarray_push(point_layer->positions, &position);
373 dynarray_push(point_layer->colors, &color);
374 dynarray_push(point_layer->ids, id);
378 create_undo_context(point_layer, UNDO_ADD));
384 void point_layer_swap_elements(PointLayer *point_layer,
386 UndoHistory *undo_history)
388 trace_assert(point_layer);
389 trace_assert(undo_history);
390 trace_assert(a < dynarray_count(point_layer->positions));
391 trace_assert(b < dynarray_count(point_layer->positions));
393 dynarray_swap(point_layer->positions, a, b);
394 dynarray_swap(point_layer->colors, a, b);
395 dynarray_swap(point_layer->ids, a, b);
399 create_undo_swap_context(point_layer, a, b));
403 void point_layer_delete_nth_element(PointLayer *point_layer,
405 UndoHistory *undo_history)
407 trace_assert(point_layer);
415 dynarray_delete_at(point_layer->positions, i);
416 dynarray_delete_at(point_layer->colors, i);
417 dynarray_delete_at(point_layer->ids, i);
421 int point_layer_idle_event(PointLayer *point_layer,
422 const SDL_Event *event,
423 const Camera *camera,
424 UndoHistory *undo_history)
426 trace_assert(point_layer);
428 trace_assert(camera);
431 if (color_picker_event(
432 &point_layer->color_picker,
440 if (point_layer->selection >= 0) {
441 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
442 point_layer->state = POINT_LAYER_RECOLOR;
447 switch (event->type) {
448 case SDL_MOUSEBUTTONDOWN: {
449 switch (event->button.button) {
450 case SDL_BUTTON_LEFT: {
451 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
453 point_layer->selection = point_layer_element_at(
454 point_layer, position);
456 if (point_layer->selection < 0) {
457 point_layer_add_element(
460 color_picker_rgba(&point_layer->color_picker),
463 Color *colors = dynarray_data(point_layer->colors);
464 Vec2f *positions = dynarray_data(point_layer->positions);
466 point_layer->state = POINT_LAYER_MOVE;
467 point_layer->color_picker =
468 create_color_picker_from_rgba(colors[point_layer->selection]);
469 point_layer->inter_position = positions[point_layer->selection];
476 switch (event->key.keysym.sym) {
478 if ((event->key.keysym.mod & KMOD_SHIFT)
479 && (point_layer->selection >= 0)
480 && ((size_t)(point_layer->selection + 1) < dynarray_count(point_layer->positions))) {
481 point_layer_swap_elements(
483 (size_t) point_layer->selection,
484 (size_t) point_layer->selection + 1,
486 point_layer->selection++;
491 if ((event->key.keysym.mod & KMOD_SHIFT)
492 && (point_layer->selection > 0)
493 && ((size_t) point_layer->selection < dynarray_count(point_layer->positions))) {
494 point_layer_swap_elements(
496 (size_t) point_layer->selection,
497 (size_t) point_layer->selection - 1,
499 point_layer->selection--;
504 if (0 <= point_layer->selection && point_layer->selection < (int) dynarray_count(point_layer->positions)) {
505 point_layer_delete_nth_element(
507 (size_t)point_layer->selection,
509 point_layer->selection = -1;
514 if (point_layer->selection >= 0) {
515 char *ids = dynarray_data(point_layer->ids);
516 point_layer->state = POINT_LAYER_EDIT_ID;
518 point_layer->edit_field,
519 ids + ID_MAX_SIZE * point_layer->selection);
520 SDL_StartTextInput();
525 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
527 dynarray_copy_to(point_layer->colors, &clipboard_color, (size_t)point_layer->selection);
532 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
534 SDL_GetMouseState(&x, &y);
535 Vec2f position = camera_map_screen(camera, x, y);
537 point_layer_add_element(
552 int point_layer_edit_id_event(PointLayer *point_layer,
553 const SDL_Event *event,
554 const Camera *camera,
555 UndoHistory *undo_history)
557 trace_assert(point_layer);
559 trace_assert(camera);
561 switch (event->type) {
563 switch(event->key.keysym.sym) {
571 char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selection);
572 const char *text = edit_field_as_text(point_layer->edit_field);
573 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
575 memset(id + n, 0, ID_MAX_SIZE - n);
577 point_layer->state = POINT_LAYER_IDLE;
583 point_layer->state = POINT_LAYER_IDLE;
591 return edit_field_event(point_layer->edit_field, event);
595 int point_layer_move_event(PointLayer *point_layer,
596 const SDL_Event *event,
597 const Camera *camera,
598 UndoHistory *undo_history)
600 trace_assert(point_layer);
602 trace_assert(camera);
603 trace_assert(point_layer->selection >= 0);
605 Vec2f *positions = dynarray_data(point_layer->positions);
607 switch (event->type) {
608 case SDL_MOUSEBUTTONUP: {
609 switch (event->button.button) {
610 case SDL_BUTTON_LEFT: {
611 point_layer->state = POINT_LAYER_IDLE;
613 const float distance = vec_length(
614 vec_sub(point_layer->inter_position,
615 positions[point_layer->selection]));
617 if (distance > 1e-6) {
625 point_layer->positions,
626 (size_t) point_layer->selection,
627 &point_layer->inter_position);
633 case SDL_MOUSEMOTION: {
634 const Uint8 *state = SDL_GetKeyboardState(NULL);
635 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
636 const Vec2f point_pos = positions[point_layer->selection];
638 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
639 point_layer->inter_position = mouse_pos;
641 const float dx = fabsf(point_pos.x - mouse_pos.x);
642 const float dy = fabsf(point_pos.y - mouse_pos.y);
645 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
647 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
657 int point_layer_recolor_event(PointLayer *point_layer,
658 const SDL_Event *event,
659 const Camera *camera,
660 UndoHistory *undo_history)
662 trace_assert(point_layer);
664 trace_assert(camera);
665 trace_assert(undo_history);
666 trace_assert(point_layer->selection >= 0);
669 if (color_picker_event(
670 &point_layer->color_picker,
678 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
680 if (!color_picker_drag(&point_layer->color_picker)) {
689 (size_t) point_layer->selection,
690 &point_layer->inter_color);
692 point_layer->state = POINT_LAYER_IDLE;
700 int point_layer_event(PointLayer *point_layer,
701 const SDL_Event *event,
702 const Camera *camera,
703 UndoHistory *undo_history)
705 trace_assert(point_layer);
707 trace_assert(camera);
708 trace_assert(undo_history);
710 switch (point_layer->state) {
711 case POINT_LAYER_IDLE:
712 return point_layer_idle_event(point_layer, event, camera, undo_history);
714 case POINT_LAYER_EDIT_ID:
715 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
717 case POINT_LAYER_MOVE:
718 return point_layer_move_event(point_layer, event, camera, undo_history);
720 case POINT_LAYER_RECOLOR:
721 return point_layer_recolor_event(point_layer, event, camera, undo_history);
727 size_t point_layer_count(const PointLayer *point_layer)
729 trace_assert(point_layer);
730 return dynarray_count(point_layer->positions);
733 const Vec2f *point_layer_positions(const PointLayer *point_layer)
735 trace_assert(point_layer);
736 return dynarray_data(point_layer->positions);
739 const Color *point_layer_colors(const PointLayer *point_layer)
741 trace_assert(point_layer);
742 return dynarray_data(point_layer->colors);
745 const char *point_layer_ids(const PointLayer *point_layer)
747 trace_assert(point_layer);
748 return dynarray_data(point_layer->ids);
751 int point_layer_dump_stream(const PointLayer *point_layer,
754 trace_assert(point_layer);
755 trace_assert(filedump);
757 size_t n = dynarray_count(point_layer->ids);
758 char *ids = dynarray_data(point_layer->ids);
759 Vec2f *positions = dynarray_data(point_layer->positions);
760 Color *colors = dynarray_data(point_layer->colors);
762 fprintf(filedump, "%zd\n", n);
763 for (size_t i = 0; i < n; ++i) {
764 fprintf(filedump, "%s %f %f ",
765 ids + ID_MAX_SIZE * i,
766 positions[i].x, positions[i].y);
767 color_hex_to_stream(colors[i], filedump);
768 fprintf(filedump, "\n");