6 #include "game/camera.h"
7 #include "system/log.h"
8 #include "system/nth_alloc.h"
9 #include "system/stacktrace.h"
10 #include "system/str.h"
11 #include "ui/edit_field.h"
12 #include "./point_layer.h"
13 #include "math/extrema.h"
14 #include "math/mat3x3.h"
15 #include "./color_picker.h"
16 #include "undo_history.h"
18 #define POINT_LAYER_ELEMENT_RADIUS 10.0f
19 #define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
20 #define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
22 static int point_clipboard = 0;
23 static Color point_clipboard_color;
25 // TODO(#1140): PointLayer does not support snapping
45 PointUndoContext create_point_undo_swap_context(PointLayer *point_layer,
46 size_t index, size_t index2)
48 trace_assert(point_layer);
49 trace_assert(index < point_layer->positions.count);
50 trace_assert(index2 < point_layer->positions.count);
52 PointUndoContext undo_context;
53 undo_context.type = POINT_UNDO_SWAP;
54 undo_context.layer = point_layer;
55 undo_context.index = index;
56 undo_context.index2 = index2;
61 PointUndoContext create_point_undo_context(PointLayer *point_layer,
64 trace_assert(type != POINT_UNDO_SWAP);
66 (void) create_point_undo_swap_context;
68 PointUndoContext undo_context;
71 type == POINT_UNDO_ADD
72 ? point_layer->positions.count - 1
73 : (size_t) point_layer->selection;
75 undo_context.type = type;
76 undo_context.layer = point_layer;
77 dynarray_copy_to(&point_layer->positions, &undo_context.position, index);
78 dynarray_copy_to(&point_layer->colors, &undo_context.color, index);
79 dynarray_copy_to(&point_layer->ids, &undo_context.id, index);
80 undo_context.index = index;
86 void point_layer_undo(void *context, size_t context_size)
88 trace_assert(context);
89 trace_assert(sizeof(PointUndoContext) == context_size);
91 PointUndoContext *undo_context = context;
92 PointLayer *point_layer = undo_context->layer;
94 switch (undo_context->type) {
95 case POINT_UNDO_ADD: {
96 dynarray_pop(&point_layer->positions, NULL);
97 dynarray_pop(&point_layer->colors, NULL);
98 dynarray_pop(&point_layer->ids, NULL);
99 point_layer->selection = -1;
102 case POINT_UNDO_DELETE: {
103 dynarray_insert_before(&point_layer->positions, undo_context->index, &undo_context->position);
104 dynarray_insert_before(&point_layer->colors, undo_context->index, &undo_context->color);
105 dynarray_insert_before(&point_layer->ids, undo_context->index, &undo_context->id);
106 point_layer->selection = -1;
109 case POINT_UNDO_UPDATE: {
110 dynarray_replace_at(&point_layer->positions, undo_context->index, &undo_context->position);
111 dynarray_replace_at(&point_layer->colors, undo_context->index, &undo_context->color);
112 dynarray_replace_at(&point_layer->ids, undo_context->index, &undo_context->id);
115 case POINT_UNDO_SWAP: {
116 dynarray_swap(&point_layer->positions, undo_context->index, undo_context->index2);
117 dynarray_swap(&point_layer->colors, undo_context->index, undo_context->index2);
118 dynarray_swap(&point_layer->ids, undo_context->index, undo_context->index2);
123 #define POINT_UNDO_PUSH(HISTORY, CONTEXT) \
125 PointUndoContext context = (CONTEXT); \
133 LayerPtr point_layer_as_layer(PointLayer *point_layer)
142 PointLayer create_point_layer(const char *id_name_prefix)
144 PointLayer result = {0};
145 result.state = POINT_LAYER_IDLE;
146 result.positions = create_dynarray(sizeof(Vec2f));
147 result.colors = create_dynarray(sizeof(Color));
148 result.ids = create_dynarray(sizeof(char) * ID_MAX_SIZE);
149 result.edit_field.font_size = POINT_LAYER_ID_TEXT_SIZE;
150 result.edit_field.font_color = POINT_LAYER_ID_TEXT_COLOR;
151 result.id_name_prefix = id_name_prefix;
155 void point_layer_reload(PointLayer *point_layer,
159 trace_assert(point_layer);
160 trace_assert(memory);
163 point_layer_clean(point_layer);
165 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
166 char id[ENTITY_MAX_ID_SIZE];
167 for (int i = 0; i < n; ++i) {
168 String line = trim(chop_by_delim(input, '\n'));
169 String string_id = trim(chop_word(&line));
171 point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
172 point.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
173 Color color = hexs(trim(chop_word(&line)));
175 memset(id, 0, ENTITY_MAX_ID_SIZE);
176 memcpy(id, string_id.data, min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
178 dynarray_push(&point_layer->positions, &point);
179 dynarray_push(&point_layer->colors, &color);
180 dynarray_push(&point_layer->ids, id);
184 void point_layer_clean(PointLayer *point_layer)
186 trace_assert(point_layer);
187 dynarray_clear(&point_layer->positions);
188 dynarray_clear(&point_layer->colors);
189 dynarray_clear(&point_layer->ids);
193 Triangle element_shape(Vec2f position, float scale)
195 return triangle_mat3x3_product(
196 equilateral_triangle(),
198 trans_mat_vec(position),
202 int point_layer_render(const PointLayer *point_layer,
203 const Camera *camera,
206 trace_assert(point_layer);
207 trace_assert(camera);
209 const int n = (int)point_layer->positions.count;
210 Vec2f *positions = (Vec2f *)point_layer->positions.data;
211 Color *colors = (Color *)point_layer->colors.data;
212 char *ids = (char *)point_layer->ids.data;
214 for (int i = 0; i < n; ++i) {
215 const Color color = color_scale(
216 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
217 ? point_layer->inter_color
219 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
221 const Vec2f position =
222 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
223 ? point_layer->inter_position
227 if (active && i == point_layer->selection) {
228 if (camera_fill_triangle(
232 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
233 color_invert(color)) < 0) {
237 if (point_layer->state != POINT_LAYER_EDIT_ID &&
240 ids + ID_MAX_SIZE * i,
241 POINT_LAYER_ID_TEXT_SIZE,
242 POINT_LAYER_ID_TEXT_COLOR,
248 if (camera_fill_triangle(
252 POINT_LAYER_ELEMENT_RADIUS),
258 if (point_layer->state == POINT_LAYER_EDIT_ID) {
259 if (edit_field_render_world(
260 &point_layer->edit_field,
262 positions[point_layer->selection]) < 0) {
267 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
276 int point_layer_element_at(const PointLayer *point_layer,
279 trace_assert(point_layer);
281 int n = (int) point_layer->positions.count;
282 Vec2f *positions = (Vec2f *)point_layer->positions.data;
284 for (int i = n - 1; i >= 0; --i) {
285 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
294 int point_layer_add_element(PointLayer *point_layer,
297 UndoHistory *undo_history)
299 trace_assert(point_layer);
300 trace_assert(undo_history);
302 char id[ID_MAX_SIZE];
303 snprintf(id, ID_MAX_SIZE, "%s_%d",
304 point_layer->id_name_prefix,
305 point_layer->id_name_counter++);
307 dynarray_push(&point_layer->positions, &position);
308 dynarray_push(&point_layer->colors, &color);
309 dynarray_push(&point_layer->ids, id);
313 create_point_undo_context(point_layer, POINT_UNDO_ADD));
319 void point_layer_swap_elements(PointLayer *point_layer,
321 UndoHistory *undo_history)
323 trace_assert(point_layer);
324 trace_assert(undo_history);
325 trace_assert(a < point_layer->positions.count);
326 trace_assert(b < point_layer->positions.count);
328 dynarray_swap(&point_layer->positions, a, b);
329 dynarray_swap(&point_layer->colors, a, b);
330 dynarray_swap(&point_layer->ids, a, b);
334 create_point_undo_swap_context(point_layer, a, b));
338 void point_layer_delete_nth_element(PointLayer *point_layer,
340 UndoHistory *undo_history)
342 trace_assert(point_layer);
346 create_point_undo_context(
350 dynarray_delete_at(&point_layer->positions, i);
351 dynarray_delete_at(&point_layer->colors, i);
352 dynarray_delete_at(&point_layer->ids, i);
356 int point_layer_idle_event(PointLayer *point_layer,
357 const SDL_Event *event,
358 const Camera *camera,
359 UndoHistory *undo_history)
361 trace_assert(point_layer);
363 trace_assert(camera);
366 if (color_picker_event(
367 &point_layer->color_picker,
375 if (point_layer->selection >= 0) {
376 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
377 point_layer->state = POINT_LAYER_RECOLOR;
382 switch (event->type) {
383 case SDL_MOUSEBUTTONDOWN: {
384 switch (event->button.button) {
385 case SDL_BUTTON_LEFT: {
386 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
388 point_layer->selection = point_layer_element_at(
389 point_layer, position);
391 if (point_layer->selection < 0) {
392 point_layer_add_element(
395 color_picker_rgba(&point_layer->color_picker),
398 Color *colors = (Color*)point_layer->colors.data;
399 Vec2f *positions = (Vec2f*)point_layer->positions.data;
401 point_layer->state = POINT_LAYER_MOVE;
402 point_layer->color_picker =
403 create_color_picker_from_rgba(colors[point_layer->selection]);
404 point_layer->inter_position = positions[point_layer->selection];
411 switch (event->key.keysym.sym) {
413 if ((event->key.keysym.mod & KMOD_SHIFT)
414 && (point_layer->selection >= 0)
415 && ((size_t)(point_layer->selection + 1) < point_layer->positions.count)) {
416 point_layer_swap_elements(
418 (size_t) point_layer->selection,
419 (size_t) point_layer->selection + 1,
421 point_layer->selection++;
426 if ((event->key.keysym.mod & KMOD_SHIFT)
427 && (point_layer->selection > 0)
428 && ((size_t) point_layer->selection < point_layer->positions.count)) {
429 point_layer_swap_elements(
431 (size_t) point_layer->selection,
432 (size_t) point_layer->selection - 1,
434 point_layer->selection--;
439 if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
440 point_layer_delete_nth_element(
442 (size_t)point_layer->selection,
444 point_layer->selection = -1;
449 if (point_layer->selection >= 0) {
450 char *ids = (char*)point_layer->ids.data;
451 point_layer->state = POINT_LAYER_EDIT_ID;
453 &point_layer->edit_field,
454 ids + ID_MAX_SIZE * point_layer->selection);
455 SDL_StartTextInput();
460 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
462 dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
467 if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
469 SDL_GetMouseState(&x, &y);
470 Vec2f position = camera_map_screen(camera, x, y);
472 point_layer_add_element(
475 point_clipboard_color,
487 int point_layer_edit_id_event(PointLayer *point_layer,
488 const SDL_Event *event,
489 const Camera *camera,
490 UndoHistory *undo_history)
492 trace_assert(point_layer);
494 trace_assert(camera);
496 switch (event->type) {
498 switch(event->key.keysym.sym) {
502 create_point_undo_context(
506 char *id = dynarray_pointer_at(&point_layer->ids, (size_t) point_layer->selection);
507 const char *text = edit_field_as_text(&point_layer->edit_field);
508 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
510 memset(id + n, 0, ID_MAX_SIZE - n);
512 point_layer->state = POINT_LAYER_IDLE;
518 point_layer->state = POINT_LAYER_IDLE;
526 return edit_field_event(&point_layer->edit_field, event);
530 int point_layer_move_event(PointLayer *point_layer,
531 const SDL_Event *event,
532 const Camera *camera,
533 UndoHistory *undo_history)
535 trace_assert(point_layer);
537 trace_assert(camera);
538 trace_assert(point_layer->selection >= 0);
540 Vec2f *positions = (Vec2f*)point_layer->positions.data;
542 switch (event->type) {
543 case SDL_MOUSEBUTTONUP: {
544 switch (event->button.button) {
545 case SDL_BUTTON_LEFT: {
546 point_layer->state = POINT_LAYER_IDLE;
548 const float distance = vec_length(
549 vec_sub(point_layer->inter_position,
550 positions[point_layer->selection]));
552 if (distance > 1e-6) {
555 create_point_undo_context(
560 &point_layer->positions,
561 (size_t) point_layer->selection,
562 &point_layer->inter_position);
568 case SDL_MOUSEMOTION: {
569 const Uint8 *state = SDL_GetKeyboardState(NULL);
570 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
571 const Vec2f point_pos = positions[point_layer->selection];
573 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
574 point_layer->inter_position = mouse_pos;
576 const float dx = fabsf(point_pos.x - mouse_pos.x);
577 const float dy = fabsf(point_pos.y - mouse_pos.y);
580 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
582 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
592 int point_layer_recolor_event(PointLayer *point_layer,
593 const SDL_Event *event,
594 const Camera *camera,
595 UndoHistory *undo_history)
597 trace_assert(point_layer);
599 trace_assert(camera);
600 trace_assert(undo_history);
601 trace_assert(point_layer->selection >= 0);
604 if (color_picker_event(
605 &point_layer->color_picker,
613 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
615 if (!color_picker_drag(&point_layer->color_picker)) {
618 create_point_undo_context(
623 &point_layer->colors,
624 (size_t) point_layer->selection,
625 &point_layer->inter_color);
627 point_layer->state = POINT_LAYER_IDLE;
635 int point_layer_event(PointLayer *point_layer,
636 const SDL_Event *event,
637 const Camera *camera,
638 UndoHistory *undo_history)
640 trace_assert(point_layer);
642 trace_assert(camera);
643 trace_assert(undo_history);
645 switch (point_layer->state) {
646 case POINT_LAYER_IDLE:
647 return point_layer_idle_event(point_layer, event, camera, undo_history);
649 case POINT_LAYER_EDIT_ID:
650 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
652 case POINT_LAYER_MOVE:
653 return point_layer_move_event(point_layer, event, camera, undo_history);
655 case POINT_LAYER_RECOLOR:
656 return point_layer_recolor_event(point_layer, event, camera, undo_history);
662 size_t point_layer_count(const PointLayer *point_layer)
664 trace_assert(point_layer);
665 return point_layer->positions.count;
668 const Vec2f *point_layer_positions(const PointLayer *point_layer)
670 trace_assert(point_layer);
671 return (const Vec2f *)point_layer->positions.data;
674 const Color *point_layer_colors(const PointLayer *point_layer)
676 trace_assert(point_layer);
677 return (const Color *)point_layer->colors.data;
680 const char *point_layer_ids(const PointLayer *point_layer)
682 trace_assert(point_layer);
683 return (const char *)point_layer->ids.data;
686 int point_layer_dump_stream(const PointLayer *point_layer,
689 trace_assert(point_layer);
690 trace_assert(filedump);
692 size_t n = point_layer->ids.count;
693 char *ids = (char *) point_layer->ids.data;
694 Vec2f *positions = (Vec2f *) point_layer->positions.data;
695 Color *colors = (Color *) point_layer->colors.data;
697 fprintf(filedump, "%zd\n", n);
698 for (size_t i = 0; i < n; ++i) {
699 fprintf(filedump, "%s %f %f ",
700 ids + ID_MAX_SIZE * i,
701 positions[i].x, positions[i].y);
702 color_hex_to_stream(colors[i], filedump);
703 fprintf(filedump, "\n");