6 #include "game/camera.h"
7 #include "system/log.h"
9 #include "system/nth_alloc.h"
10 #include "system/stacktrace.h"
11 #include "system/str.h"
12 #include "ui/edit_field.h"
13 #include "./point_layer.h"
14 #include "math/extrema.h"
15 #include "math/mat3x3.h"
16 #include "./color_picker.h"
17 #include "undo_history.h"
19 #define POINT_LAYER_ELEMENT_RADIUS 10.0f
20 #define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
21 #define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
23 static int point_clipboard = 0;
24 static Color point_clipboard_color;
26 // TODO(#1140): PointLayer does not support snapping
38 PointLayerState state;
39 Dynarray/*<Vec2f>*/ positions;
40 Dynarray/*<Color>*/ colors;
41 Dynarray/*<char[ID_MAX_SIZE]>*/ ids;
43 ColorPicker color_picker;
47 Edit_field *edit_field;
50 const char *id_name_prefix;
71 PointUndoContext create_point_undo_swap_context(PointLayer *point_layer,
72 size_t index, size_t index2)
74 trace_assert(point_layer);
75 trace_assert(index < point_layer->positions.count);
76 trace_assert(index2 < point_layer->positions.count);
78 PointUndoContext undo_context;
79 undo_context.type = POINT_UNDO_SWAP;
80 undo_context.layer = point_layer;
81 undo_context.index = index;
82 undo_context.index2 = index2;
87 PointUndoContext create_point_undo_context(PointLayer *point_layer,
90 trace_assert(type != POINT_UNDO_SWAP);
92 (void) create_point_undo_swap_context;
94 PointUndoContext undo_context;
97 type == POINT_UNDO_ADD
98 ? point_layer->positions.count - 1
99 : (size_t) point_layer->selection;
101 undo_context.type = type;
102 undo_context.layer = point_layer;
103 dynarray_copy_to(&point_layer->positions, &undo_context.position, index);
104 dynarray_copy_to(&point_layer->colors, &undo_context.color, index);
105 dynarray_copy_to(&point_layer->ids, &undo_context.id, index);
106 undo_context.index = index;
112 void point_layer_undo(void *context, size_t context_size)
114 trace_assert(context);
115 trace_assert(sizeof(PointUndoContext) == context_size);
117 PointUndoContext *undo_context = context;
118 PointLayer *point_layer = undo_context->layer;
120 switch (undo_context->type) {
121 case POINT_UNDO_ADD: {
122 dynarray_pop(&point_layer->positions, NULL);
123 dynarray_pop(&point_layer->colors, NULL);
124 dynarray_pop(&point_layer->ids, NULL);
125 point_layer->selection = -1;
128 case POINT_UNDO_DELETE: {
129 dynarray_insert_before(&point_layer->positions, undo_context->index, &undo_context->position);
130 dynarray_insert_before(&point_layer->colors, undo_context->index, &undo_context->color);
131 dynarray_insert_before(&point_layer->ids, undo_context->index, &undo_context->id);
132 point_layer->selection = -1;
135 case POINT_UNDO_UPDATE: {
136 dynarray_replace_at(&point_layer->positions, undo_context->index, &undo_context->position);
137 dynarray_replace_at(&point_layer->colors, undo_context->index, &undo_context->color);
138 dynarray_replace_at(&point_layer->ids, undo_context->index, &undo_context->id);
141 case POINT_UNDO_SWAP: {
142 dynarray_swap(&point_layer->positions, undo_context->index, undo_context->index2);
143 dynarray_swap(&point_layer->colors, undo_context->index, undo_context->index2);
144 dynarray_swap(&point_layer->ids, undo_context->index, undo_context->index2);
149 #define POINT_UNDO_PUSH(HISTORY, CONTEXT) \
151 PointUndoContext context = (CONTEXT); \
159 LayerPtr point_layer_as_layer(PointLayer *point_layer)
168 PointLayer *create_point_layer(const char *id_name_prefix)
170 Lt *lt = create_lt();
172 PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
173 if (point_layer == NULL) {
176 point_layer->lt = lt;
178 point_layer->state = POINT_LAYER_IDLE;
180 point_layer->positions = create_dynarray(sizeof(Vec2f));
181 point_layer->colors = create_dynarray(sizeof(Color));
182 point_layer->ids = create_dynarray(sizeof(char) * ID_MAX_SIZE);
184 point_layer->edit_field = PUSH_LT(
187 POINT_LAYER_ID_TEXT_SIZE,
188 POINT_LAYER_ID_TEXT_COLOR),
190 if (point_layer->edit_field == NULL) {
194 point_layer->id_name_prefix = id_name_prefix;
199 PointLayer *chop_point_layer(Memory *memory,
201 const char *id_name_prefix)
203 trace_assert(memory);
205 trace_assert(id_name_prefix);
207 PointLayer *point_layer = create_point_layer(id_name_prefix);
209 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
210 char id[ENTITY_MAX_ID_SIZE];
211 for (int i = 0; i < n; ++i) {
212 String line = trim(chop_by_delim(input, '\n'));
213 String string_id = trim(chop_word(&line));
215 point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
216 point.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
217 Color color = hexs(trim(chop_word(&line)));
219 memset(id, 0, ENTITY_MAX_ID_SIZE);
223 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
225 dynarray_push(&point_layer->positions, &point);
226 dynarray_push(&point_layer->colors, &color);
227 dynarray_push(&point_layer->ids, id);
230 point_layer->selection = -1;
231 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
236 void destroy_point_layer(PointLayer *point_layer)
238 trace_assert(point_layer);
240 free(point_layer->positions.data);
241 free(point_layer->colors.data);
242 free(point_layer->ids.data);
244 RETURN_LT0(point_layer->lt);
248 Triangle element_shape(Vec2f position, float scale)
250 return triangle_mat3x3_product(
251 equilateral_triangle(),
253 trans_mat_vec(position),
257 int point_layer_render(const PointLayer *point_layer,
258 const Camera *camera,
261 trace_assert(point_layer);
262 trace_assert(camera);
264 const int n = (int)point_layer->positions.count;
265 Vec2f *positions = (Vec2f *)point_layer->positions.data;
266 Color *colors = (Color *)point_layer->colors.data;
267 char *ids = (char *)point_layer->ids.data;
269 for (int i = 0; i < n; ++i) {
270 const Color color = color_scale(
271 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
272 ? point_layer->inter_color
274 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
276 const Vec2f position =
277 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
278 ? point_layer->inter_position
282 if (active && i == point_layer->selection) {
283 if (camera_fill_triangle(
287 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
288 color_invert(color)) < 0) {
292 if (point_layer->state != POINT_LAYER_EDIT_ID &&
295 ids + ID_MAX_SIZE * i,
296 POINT_LAYER_ID_TEXT_SIZE,
297 POINT_LAYER_ID_TEXT_COLOR,
303 if (camera_fill_triangle(
307 POINT_LAYER_ELEMENT_RADIUS),
313 if (point_layer->state == POINT_LAYER_EDIT_ID) {
314 if (edit_field_render_world(
315 point_layer->edit_field,
317 positions[point_layer->selection]) < 0) {
322 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
331 int point_layer_element_at(const PointLayer *point_layer,
334 trace_assert(point_layer);
336 int n = (int) point_layer->positions.count;
337 Vec2f *positions = (Vec2f *)point_layer->positions.data;
339 for (int i = n - 1; i >= 0; --i) {
340 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
349 int point_layer_add_element(PointLayer *point_layer,
352 UndoHistory *undo_history)
354 trace_assert(point_layer);
355 trace_assert(undo_history);
357 char id[ID_MAX_SIZE];
358 snprintf(id, ID_MAX_SIZE, "%s_%d",
359 point_layer->id_name_prefix,
360 point_layer->id_name_counter++);
362 dynarray_push(&point_layer->positions, &position);
363 dynarray_push(&point_layer->colors, &color);
364 dynarray_push(&point_layer->ids, id);
368 create_point_undo_context(point_layer, POINT_UNDO_ADD));
374 void point_layer_swap_elements(PointLayer *point_layer,
376 UndoHistory *undo_history)
378 trace_assert(point_layer);
379 trace_assert(undo_history);
380 trace_assert(a < point_layer->positions.count);
381 trace_assert(b < point_layer->positions.count);
383 dynarray_swap(&point_layer->positions, a, b);
384 dynarray_swap(&point_layer->colors, a, b);
385 dynarray_swap(&point_layer->ids, a, b);
389 create_point_undo_swap_context(point_layer, a, b));
393 void point_layer_delete_nth_element(PointLayer *point_layer,
395 UndoHistory *undo_history)
397 trace_assert(point_layer);
401 create_point_undo_context(
405 dynarray_delete_at(&point_layer->positions, i);
406 dynarray_delete_at(&point_layer->colors, i);
407 dynarray_delete_at(&point_layer->ids, i);
411 int point_layer_idle_event(PointLayer *point_layer,
412 const SDL_Event *event,
413 const Camera *camera,
414 UndoHistory *undo_history)
416 trace_assert(point_layer);
418 trace_assert(camera);
421 if (color_picker_event(
422 &point_layer->color_picker,
430 if (point_layer->selection >= 0) {
431 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
432 point_layer->state = POINT_LAYER_RECOLOR;
437 switch (event->type) {
438 case SDL_MOUSEBUTTONDOWN: {
439 switch (event->button.button) {
440 case SDL_BUTTON_LEFT: {
441 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
443 point_layer->selection = point_layer_element_at(
444 point_layer, position);
446 if (point_layer->selection < 0) {
447 point_layer_add_element(
450 color_picker_rgba(&point_layer->color_picker),
453 Color *colors = (Color*)point_layer->colors.data;
454 Vec2f *positions = (Vec2f*)point_layer->positions.data;
456 point_layer->state = POINT_LAYER_MOVE;
457 point_layer->color_picker =
458 create_color_picker_from_rgba(colors[point_layer->selection]);
459 point_layer->inter_position = positions[point_layer->selection];
466 switch (event->key.keysym.sym) {
468 if ((event->key.keysym.mod & KMOD_SHIFT)
469 && (point_layer->selection >= 0)
470 && ((size_t)(point_layer->selection + 1) < point_layer->positions.count)) {
471 point_layer_swap_elements(
473 (size_t) point_layer->selection,
474 (size_t) point_layer->selection + 1,
476 point_layer->selection++;
481 if ((event->key.keysym.mod & KMOD_SHIFT)
482 && (point_layer->selection > 0)
483 && ((size_t) point_layer->selection < point_layer->positions.count)) {
484 point_layer_swap_elements(
486 (size_t) point_layer->selection,
487 (size_t) point_layer->selection - 1,
489 point_layer->selection--;
494 if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
495 point_layer_delete_nth_element(
497 (size_t)point_layer->selection,
499 point_layer->selection = -1;
504 if (point_layer->selection >= 0) {
505 char *ids = (char*)point_layer->ids.data;
506 point_layer->state = POINT_LAYER_EDIT_ID;
508 point_layer->edit_field,
509 ids + ID_MAX_SIZE * point_layer->selection);
510 SDL_StartTextInput();
515 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
517 dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
522 if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
524 SDL_GetMouseState(&x, &y);
525 Vec2f position = camera_map_screen(camera, x, y);
527 point_layer_add_element(
530 point_clipboard_color,
542 int point_layer_edit_id_event(PointLayer *point_layer,
543 const SDL_Event *event,
544 const Camera *camera,
545 UndoHistory *undo_history)
547 trace_assert(point_layer);
549 trace_assert(camera);
551 switch (event->type) {
553 switch(event->key.keysym.sym) {
557 create_point_undo_context(
561 char *id = dynarray_pointer_at(&point_layer->ids, (size_t) point_layer->selection);
562 const char *text = edit_field_as_text(point_layer->edit_field);
563 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
565 memset(id + n, 0, ID_MAX_SIZE - n);
567 point_layer->state = POINT_LAYER_IDLE;
573 point_layer->state = POINT_LAYER_IDLE;
581 return edit_field_event(point_layer->edit_field, event);
585 int point_layer_move_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);
593 trace_assert(point_layer->selection >= 0);
595 Vec2f *positions = (Vec2f*)point_layer->positions.data;
597 switch (event->type) {
598 case SDL_MOUSEBUTTONUP: {
599 switch (event->button.button) {
600 case SDL_BUTTON_LEFT: {
601 point_layer->state = POINT_LAYER_IDLE;
603 const float distance = vec_length(
604 vec_sub(point_layer->inter_position,
605 positions[point_layer->selection]));
607 if (distance > 1e-6) {
610 create_point_undo_context(
615 &point_layer->positions,
616 (size_t) point_layer->selection,
617 &point_layer->inter_position);
623 case SDL_MOUSEMOTION: {
624 const Uint8 *state = SDL_GetKeyboardState(NULL);
625 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
626 const Vec2f point_pos = positions[point_layer->selection];
628 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
629 point_layer->inter_position = mouse_pos;
631 const float dx = fabsf(point_pos.x - mouse_pos.x);
632 const float dy = fabsf(point_pos.y - mouse_pos.y);
635 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
637 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
647 int point_layer_recolor_event(PointLayer *point_layer,
648 const SDL_Event *event,
649 const Camera *camera,
650 UndoHistory *undo_history)
652 trace_assert(point_layer);
654 trace_assert(camera);
655 trace_assert(undo_history);
656 trace_assert(point_layer->selection >= 0);
659 if (color_picker_event(
660 &point_layer->color_picker,
668 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
670 if (!color_picker_drag(&point_layer->color_picker)) {
673 create_point_undo_context(
678 &point_layer->colors,
679 (size_t) point_layer->selection,
680 &point_layer->inter_color);
682 point_layer->state = POINT_LAYER_IDLE;
690 int point_layer_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);
700 switch (point_layer->state) {
701 case POINT_LAYER_IDLE:
702 return point_layer_idle_event(point_layer, event, camera, undo_history);
704 case POINT_LAYER_EDIT_ID:
705 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
707 case POINT_LAYER_MOVE:
708 return point_layer_move_event(point_layer, event, camera, undo_history);
710 case POINT_LAYER_RECOLOR:
711 return point_layer_recolor_event(point_layer, event, camera, undo_history);
717 size_t point_layer_count(const PointLayer *point_layer)
719 trace_assert(point_layer);
720 return point_layer->positions.count;
723 const Vec2f *point_layer_positions(const PointLayer *point_layer)
725 trace_assert(point_layer);
726 return (const Vec2f *)point_layer->positions.data;
729 const Color *point_layer_colors(const PointLayer *point_layer)
731 trace_assert(point_layer);
732 return (const Color *)point_layer->colors.data;
735 const char *point_layer_ids(const PointLayer *point_layer)
737 trace_assert(point_layer);
738 return (const char *)point_layer->ids.data;
741 int point_layer_dump_stream(const PointLayer *point_layer,
744 trace_assert(point_layer);
745 trace_assert(filedump);
747 size_t n = point_layer->ids.count;
748 char *ids = (char *) point_layer->ids.data;
749 Vec2f *positions = (Vec2f *) point_layer->positions.data;
750 Color *colors = (Color *) point_layer->colors.data;
752 fprintf(filedump, "%zd\n", n);
753 for (size_t i = 0; i < n; ++i) {
754 fprintf(filedump, "%s %f %f ",
755 ids + ID_MAX_SIZE * i,
756 positions[i].x, positions[i].y);
757 color_hex_to_stream(colors[i], filedump);
758 fprintf(filedump, "\n");