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.font_size = POINT_LAYER_ID_TEXT_SIZE;
185 point_layer->edit_field.font_color = POINT_LAYER_ID_TEXT_COLOR;
187 point_layer->id_name_prefix = id_name_prefix;
192 PointLayer *chop_point_layer(Memory *memory,
194 const char *id_name_prefix)
196 trace_assert(memory);
198 trace_assert(id_name_prefix);
200 PointLayer *point_layer = create_point_layer(id_name_prefix);
202 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
203 char id[ENTITY_MAX_ID_SIZE];
204 for (int i = 0; i < n; ++i) {
205 String line = trim(chop_by_delim(input, '\n'));
206 String string_id = trim(chop_word(&line));
208 point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
209 point.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
210 Color color = hexs(trim(chop_word(&line)));
212 memset(id, 0, ENTITY_MAX_ID_SIZE);
216 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
218 dynarray_push(&point_layer->positions, &point);
219 dynarray_push(&point_layer->colors, &color);
220 dynarray_push(&point_layer->ids, id);
223 point_layer->selection = -1;
224 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
229 void destroy_point_layer(PointLayer *point_layer)
231 trace_assert(point_layer);
233 free(point_layer->positions.data);
234 free(point_layer->colors.data);
235 free(point_layer->ids.data);
237 RETURN_LT0(point_layer->lt);
241 Triangle element_shape(Vec2f position, float scale)
243 return triangle_mat3x3_product(
244 equilateral_triangle(),
246 trans_mat_vec(position),
250 int point_layer_render(const PointLayer *point_layer,
251 const Camera *camera,
254 trace_assert(point_layer);
255 trace_assert(camera);
257 const int n = (int)point_layer->positions.count;
258 Vec2f *positions = (Vec2f *)point_layer->positions.data;
259 Color *colors = (Color *)point_layer->colors.data;
260 char *ids = (char *)point_layer->ids.data;
262 for (int i = 0; i < n; ++i) {
263 const Color color = color_scale(
264 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
265 ? point_layer->inter_color
267 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
269 const Vec2f position =
270 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
271 ? point_layer->inter_position
275 if (active && i == point_layer->selection) {
276 if (camera_fill_triangle(
280 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
281 color_invert(color)) < 0) {
285 if (point_layer->state != POINT_LAYER_EDIT_ID &&
288 ids + ID_MAX_SIZE * i,
289 POINT_LAYER_ID_TEXT_SIZE,
290 POINT_LAYER_ID_TEXT_COLOR,
296 if (camera_fill_triangle(
300 POINT_LAYER_ELEMENT_RADIUS),
306 if (point_layer->state == POINT_LAYER_EDIT_ID) {
307 if (edit_field_render_world(
308 &point_layer->edit_field,
310 positions[point_layer->selection]) < 0) {
315 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
324 int point_layer_element_at(const PointLayer *point_layer,
327 trace_assert(point_layer);
329 int n = (int) point_layer->positions.count;
330 Vec2f *positions = (Vec2f *)point_layer->positions.data;
332 for (int i = n - 1; i >= 0; --i) {
333 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
342 int point_layer_add_element(PointLayer *point_layer,
345 UndoHistory *undo_history)
347 trace_assert(point_layer);
348 trace_assert(undo_history);
350 char id[ID_MAX_SIZE];
351 snprintf(id, ID_MAX_SIZE, "%s_%d",
352 point_layer->id_name_prefix,
353 point_layer->id_name_counter++);
355 dynarray_push(&point_layer->positions, &position);
356 dynarray_push(&point_layer->colors, &color);
357 dynarray_push(&point_layer->ids, id);
361 create_point_undo_context(point_layer, POINT_UNDO_ADD));
367 void point_layer_swap_elements(PointLayer *point_layer,
369 UndoHistory *undo_history)
371 trace_assert(point_layer);
372 trace_assert(undo_history);
373 trace_assert(a < point_layer->positions.count);
374 trace_assert(b < point_layer->positions.count);
376 dynarray_swap(&point_layer->positions, a, b);
377 dynarray_swap(&point_layer->colors, a, b);
378 dynarray_swap(&point_layer->ids, a, b);
382 create_point_undo_swap_context(point_layer, a, b));
386 void point_layer_delete_nth_element(PointLayer *point_layer,
388 UndoHistory *undo_history)
390 trace_assert(point_layer);
394 create_point_undo_context(
398 dynarray_delete_at(&point_layer->positions, i);
399 dynarray_delete_at(&point_layer->colors, i);
400 dynarray_delete_at(&point_layer->ids, i);
404 int point_layer_idle_event(PointLayer *point_layer,
405 const SDL_Event *event,
406 const Camera *camera,
407 UndoHistory *undo_history)
409 trace_assert(point_layer);
411 trace_assert(camera);
414 if (color_picker_event(
415 &point_layer->color_picker,
423 if (point_layer->selection >= 0) {
424 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
425 point_layer->state = POINT_LAYER_RECOLOR;
430 switch (event->type) {
431 case SDL_MOUSEBUTTONDOWN: {
432 switch (event->button.button) {
433 case SDL_BUTTON_LEFT: {
434 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
436 point_layer->selection = point_layer_element_at(
437 point_layer, position);
439 if (point_layer->selection < 0) {
440 point_layer_add_element(
443 color_picker_rgba(&point_layer->color_picker),
446 Color *colors = (Color*)point_layer->colors.data;
447 Vec2f *positions = (Vec2f*)point_layer->positions.data;
449 point_layer->state = POINT_LAYER_MOVE;
450 point_layer->color_picker =
451 create_color_picker_from_rgba(colors[point_layer->selection]);
452 point_layer->inter_position = positions[point_layer->selection];
459 switch (event->key.keysym.sym) {
461 if ((event->key.keysym.mod & KMOD_SHIFT)
462 && (point_layer->selection >= 0)
463 && ((size_t)(point_layer->selection + 1) < point_layer->positions.count)) {
464 point_layer_swap_elements(
466 (size_t) point_layer->selection,
467 (size_t) point_layer->selection + 1,
469 point_layer->selection++;
474 if ((event->key.keysym.mod & KMOD_SHIFT)
475 && (point_layer->selection > 0)
476 && ((size_t) point_layer->selection < point_layer->positions.count)) {
477 point_layer_swap_elements(
479 (size_t) point_layer->selection,
480 (size_t) point_layer->selection - 1,
482 point_layer->selection--;
487 if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
488 point_layer_delete_nth_element(
490 (size_t)point_layer->selection,
492 point_layer->selection = -1;
497 if (point_layer->selection >= 0) {
498 char *ids = (char*)point_layer->ids.data;
499 point_layer->state = POINT_LAYER_EDIT_ID;
501 &point_layer->edit_field,
502 ids + ID_MAX_SIZE * point_layer->selection);
503 SDL_StartTextInput();
508 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
510 dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
515 if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
517 SDL_GetMouseState(&x, &y);
518 Vec2f position = camera_map_screen(camera, x, y);
520 point_layer_add_element(
523 point_clipboard_color,
535 int point_layer_edit_id_event(PointLayer *point_layer,
536 const SDL_Event *event,
537 const Camera *camera,
538 UndoHistory *undo_history)
540 trace_assert(point_layer);
542 trace_assert(camera);
544 switch (event->type) {
546 switch(event->key.keysym.sym) {
550 create_point_undo_context(
554 char *id = dynarray_pointer_at(&point_layer->ids, (size_t) point_layer->selection);
555 const char *text = edit_field_as_text(&point_layer->edit_field);
556 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
558 memset(id + n, 0, ID_MAX_SIZE - n);
560 point_layer->state = POINT_LAYER_IDLE;
566 point_layer->state = POINT_LAYER_IDLE;
574 return edit_field_event(&point_layer->edit_field, event);
578 int point_layer_move_event(PointLayer *point_layer,
579 const SDL_Event *event,
580 const Camera *camera,
581 UndoHistory *undo_history)
583 trace_assert(point_layer);
585 trace_assert(camera);
586 trace_assert(point_layer->selection >= 0);
588 Vec2f *positions = (Vec2f*)point_layer->positions.data;
590 switch (event->type) {
591 case SDL_MOUSEBUTTONUP: {
592 switch (event->button.button) {
593 case SDL_BUTTON_LEFT: {
594 point_layer->state = POINT_LAYER_IDLE;
596 const float distance = vec_length(
597 vec_sub(point_layer->inter_position,
598 positions[point_layer->selection]));
600 if (distance > 1e-6) {
603 create_point_undo_context(
608 &point_layer->positions,
609 (size_t) point_layer->selection,
610 &point_layer->inter_position);
616 case SDL_MOUSEMOTION: {
617 const Uint8 *state = SDL_GetKeyboardState(NULL);
618 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
619 const Vec2f point_pos = positions[point_layer->selection];
621 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
622 point_layer->inter_position = mouse_pos;
624 const float dx = fabsf(point_pos.x - mouse_pos.x);
625 const float dy = fabsf(point_pos.y - mouse_pos.y);
628 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
630 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
640 int point_layer_recolor_event(PointLayer *point_layer,
641 const SDL_Event *event,
642 const Camera *camera,
643 UndoHistory *undo_history)
645 trace_assert(point_layer);
647 trace_assert(camera);
648 trace_assert(undo_history);
649 trace_assert(point_layer->selection >= 0);
652 if (color_picker_event(
653 &point_layer->color_picker,
661 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
663 if (!color_picker_drag(&point_layer->color_picker)) {
666 create_point_undo_context(
671 &point_layer->colors,
672 (size_t) point_layer->selection,
673 &point_layer->inter_color);
675 point_layer->state = POINT_LAYER_IDLE;
683 int point_layer_event(PointLayer *point_layer,
684 const SDL_Event *event,
685 const Camera *camera,
686 UndoHistory *undo_history)
688 trace_assert(point_layer);
690 trace_assert(camera);
691 trace_assert(undo_history);
693 switch (point_layer->state) {
694 case POINT_LAYER_IDLE:
695 return point_layer_idle_event(point_layer, event, camera, undo_history);
697 case POINT_LAYER_EDIT_ID:
698 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
700 case POINT_LAYER_MOVE:
701 return point_layer_move_event(point_layer, event, camera, undo_history);
703 case POINT_LAYER_RECOLOR:
704 return point_layer_recolor_event(point_layer, event, camera, undo_history);
710 size_t point_layer_count(const PointLayer *point_layer)
712 trace_assert(point_layer);
713 return point_layer->positions.count;
716 const Vec2f *point_layer_positions(const PointLayer *point_layer)
718 trace_assert(point_layer);
719 return (const Vec2f *)point_layer->positions.data;
722 const Color *point_layer_colors(const PointLayer *point_layer)
724 trace_assert(point_layer);
725 return (const Color *)point_layer->colors.data;
728 const char *point_layer_ids(const PointLayer *point_layer)
730 trace_assert(point_layer);
731 return (const char *)point_layer->ids.data;
734 int point_layer_dump_stream(const PointLayer *point_layer,
737 trace_assert(point_layer);
738 trace_assert(filedump);
740 size_t n = point_layer->ids.count;
741 char *ids = (char *) point_layer->ids.data;
742 Vec2f *positions = (Vec2f *) point_layer->positions.data;
743 Color *colors = (Color *) point_layer->colors.data;
745 fprintf(filedump, "%zd\n", n);
746 for (size_t i = 0; i < n; ++i) {
747 fprintf(filedump, "%s %f %f ",
748 ids + ID_MAX_SIZE * i,
749 positions[i].x, positions[i].y);
750 color_hex_to_stream(colors[i], filedump);
751 fprintf(filedump, "\n");