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
46 PointUndoContext create_point_undo_swap_context(PointLayer *point_layer,
47 size_t index, size_t index2)
49 trace_assert(point_layer);
50 trace_assert(index < point_layer->positions.count);
51 trace_assert(index2 < point_layer->positions.count);
53 PointUndoContext undo_context;
54 undo_context.type = POINT_UNDO_SWAP;
55 undo_context.layer = point_layer;
56 undo_context.index = index;
57 undo_context.index2 = index2;
62 PointUndoContext create_point_undo_context(PointLayer *point_layer,
65 trace_assert(type != POINT_UNDO_SWAP);
67 (void) create_point_undo_swap_context;
69 PointUndoContext undo_context;
72 type == POINT_UNDO_ADD
73 ? point_layer->positions.count - 1
74 : (size_t) point_layer->selection;
76 undo_context.type = type;
77 undo_context.layer = point_layer;
78 dynarray_copy_to(&point_layer->positions, &undo_context.position, index);
79 dynarray_copy_to(&point_layer->colors, &undo_context.color, index);
80 dynarray_copy_to(&point_layer->ids, &undo_context.id, index);
81 undo_context.index = index;
87 void point_layer_undo(void *context, size_t context_size)
89 trace_assert(context);
90 trace_assert(sizeof(PointUndoContext) == context_size);
92 PointUndoContext *undo_context = context;
93 PointLayer *point_layer = undo_context->layer;
95 switch (undo_context->type) {
96 case POINT_UNDO_ADD: {
97 dynarray_pop(&point_layer->positions, NULL);
98 dynarray_pop(&point_layer->colors, NULL);
99 dynarray_pop(&point_layer->ids, NULL);
100 point_layer->selection = -1;
103 case POINT_UNDO_DELETE: {
104 dynarray_insert_before(&point_layer->positions, undo_context->index, &undo_context->position);
105 dynarray_insert_before(&point_layer->colors, undo_context->index, &undo_context->color);
106 dynarray_insert_before(&point_layer->ids, undo_context->index, &undo_context->id);
107 point_layer->selection = -1;
110 case POINT_UNDO_UPDATE: {
111 dynarray_replace_at(&point_layer->positions, undo_context->index, &undo_context->position);
112 dynarray_replace_at(&point_layer->colors, undo_context->index, &undo_context->color);
113 dynarray_replace_at(&point_layer->ids, undo_context->index, &undo_context->id);
116 case POINT_UNDO_SWAP: {
117 dynarray_swap(&point_layer->positions, undo_context->index, undo_context->index2);
118 dynarray_swap(&point_layer->colors, undo_context->index, undo_context->index2);
119 dynarray_swap(&point_layer->ids, undo_context->index, undo_context->index2);
124 #define POINT_UNDO_PUSH(HISTORY, CONTEXT) \
126 PointUndoContext context = (CONTEXT); \
134 LayerPtr point_layer_as_layer(PointLayer *point_layer)
143 PointLayer create_point_layer(const char *id_name_prefix)
145 PointLayer result = {0};
146 result.state = POINT_LAYER_IDLE;
147 result.positions = create_dynarray(sizeof(Vec2f));
148 result.colors = create_dynarray(sizeof(Color));
149 result.ids = create_dynarray(sizeof(char) * ID_MAX_SIZE);
150 result.edit_field.font_size = POINT_LAYER_ID_TEXT_SIZE;
151 result.edit_field.font_color = POINT_LAYER_ID_TEXT_COLOR;
152 result.id_name_prefix = id_name_prefix;
156 PointLayer chop_point_layer(Memory *memory,
158 const char *id_name_prefix)
160 trace_assert(memory);
162 trace_assert(id_name_prefix);
164 PointLayer result = create_point_layer(id_name_prefix);
166 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
167 char id[ENTITY_MAX_ID_SIZE];
168 for (int i = 0; i < n; ++i) {
169 String line = trim(chop_by_delim(input, '\n'));
170 String string_id = trim(chop_word(&line));
172 point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
173 point.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
174 Color color = hexs(trim(chop_word(&line)));
176 memset(id, 0, ENTITY_MAX_ID_SIZE);
177 memcpy(id, string_id.data, min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
179 dynarray_push(&result.positions, &point);
180 dynarray_push(&result.colors, &color);
181 dynarray_push(&result.ids, id);
184 result.selection = -1;
185 result.color_picker = create_color_picker_from_rgba(COLOR_RED);
191 Triangle element_shape(Vec2f position, float scale)
193 return triangle_mat3x3_product(
194 equilateral_triangle(),
196 trans_mat_vec(position),
200 int point_layer_render(const PointLayer *point_layer,
201 const Camera *camera,
204 trace_assert(point_layer);
205 trace_assert(camera);
207 const int n = (int)point_layer->positions.count;
208 Vec2f *positions = (Vec2f *)point_layer->positions.data;
209 Color *colors = (Color *)point_layer->colors.data;
210 char *ids = (char *)point_layer->ids.data;
212 for (int i = 0; i < n; ++i) {
213 const Color color = color_scale(
214 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
215 ? point_layer->inter_color
217 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
219 const Vec2f position =
220 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
221 ? point_layer->inter_position
225 if (active && i == point_layer->selection) {
226 if (camera_fill_triangle(
230 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
231 color_invert(color)) < 0) {
235 if (point_layer->state != POINT_LAYER_EDIT_ID &&
238 ids + ID_MAX_SIZE * i,
239 POINT_LAYER_ID_TEXT_SIZE,
240 POINT_LAYER_ID_TEXT_COLOR,
246 if (camera_fill_triangle(
250 POINT_LAYER_ELEMENT_RADIUS),
256 if (point_layer->state == POINT_LAYER_EDIT_ID) {
257 if (edit_field_render_world(
258 &point_layer->edit_field,
260 positions[point_layer->selection]) < 0) {
265 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
274 int point_layer_element_at(const PointLayer *point_layer,
277 trace_assert(point_layer);
279 int n = (int) point_layer->positions.count;
280 Vec2f *positions = (Vec2f *)point_layer->positions.data;
282 for (int i = n - 1; i >= 0; --i) {
283 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
292 int point_layer_add_element(PointLayer *point_layer,
295 UndoHistory *undo_history)
297 trace_assert(point_layer);
298 trace_assert(undo_history);
300 char id[ID_MAX_SIZE];
301 snprintf(id, ID_MAX_SIZE, "%s_%d",
302 point_layer->id_name_prefix,
303 point_layer->id_name_counter++);
305 dynarray_push(&point_layer->positions, &position);
306 dynarray_push(&point_layer->colors, &color);
307 dynarray_push(&point_layer->ids, id);
311 create_point_undo_context(point_layer, POINT_UNDO_ADD));
317 void point_layer_swap_elements(PointLayer *point_layer,
319 UndoHistory *undo_history)
321 trace_assert(point_layer);
322 trace_assert(undo_history);
323 trace_assert(a < point_layer->positions.count);
324 trace_assert(b < point_layer->positions.count);
326 dynarray_swap(&point_layer->positions, a, b);
327 dynarray_swap(&point_layer->colors, a, b);
328 dynarray_swap(&point_layer->ids, a, b);
332 create_point_undo_swap_context(point_layer, a, b));
336 void point_layer_delete_nth_element(PointLayer *point_layer,
338 UndoHistory *undo_history)
340 trace_assert(point_layer);
344 create_point_undo_context(
348 dynarray_delete_at(&point_layer->positions, i);
349 dynarray_delete_at(&point_layer->colors, i);
350 dynarray_delete_at(&point_layer->ids, i);
354 int point_layer_idle_event(PointLayer *point_layer,
355 const SDL_Event *event,
356 const Camera *camera,
357 UndoHistory *undo_history)
359 trace_assert(point_layer);
361 trace_assert(camera);
364 if (color_picker_event(
365 &point_layer->color_picker,
373 if (point_layer->selection >= 0) {
374 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
375 point_layer->state = POINT_LAYER_RECOLOR;
380 switch (event->type) {
381 case SDL_MOUSEBUTTONDOWN: {
382 switch (event->button.button) {
383 case SDL_BUTTON_LEFT: {
384 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
386 point_layer->selection = point_layer_element_at(
387 point_layer, position);
389 if (point_layer->selection < 0) {
390 point_layer_add_element(
393 color_picker_rgba(&point_layer->color_picker),
396 Color *colors = (Color*)point_layer->colors.data;
397 Vec2f *positions = (Vec2f*)point_layer->positions.data;
399 point_layer->state = POINT_LAYER_MOVE;
400 point_layer->color_picker =
401 create_color_picker_from_rgba(colors[point_layer->selection]);
402 point_layer->inter_position = positions[point_layer->selection];
409 switch (event->key.keysym.sym) {
411 if ((event->key.keysym.mod & KMOD_SHIFT)
412 && (point_layer->selection >= 0)
413 && ((size_t)(point_layer->selection + 1) < point_layer->positions.count)) {
414 point_layer_swap_elements(
416 (size_t) point_layer->selection,
417 (size_t) point_layer->selection + 1,
419 point_layer->selection++;
424 if ((event->key.keysym.mod & KMOD_SHIFT)
425 && (point_layer->selection > 0)
426 && ((size_t) point_layer->selection < point_layer->positions.count)) {
427 point_layer_swap_elements(
429 (size_t) point_layer->selection,
430 (size_t) point_layer->selection - 1,
432 point_layer->selection--;
437 if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
438 point_layer_delete_nth_element(
440 (size_t)point_layer->selection,
442 point_layer->selection = -1;
447 if (point_layer->selection >= 0) {
448 char *ids = (char*)point_layer->ids.data;
449 point_layer->state = POINT_LAYER_EDIT_ID;
451 &point_layer->edit_field,
452 ids + ID_MAX_SIZE * point_layer->selection);
453 SDL_StartTextInput();
458 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
460 dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
465 if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
467 SDL_GetMouseState(&x, &y);
468 Vec2f position = camera_map_screen(camera, x, y);
470 point_layer_add_element(
473 point_clipboard_color,
485 int point_layer_edit_id_event(PointLayer *point_layer,
486 const SDL_Event *event,
487 const Camera *camera,
488 UndoHistory *undo_history)
490 trace_assert(point_layer);
492 trace_assert(camera);
494 switch (event->type) {
496 switch(event->key.keysym.sym) {
500 create_point_undo_context(
504 char *id = dynarray_pointer_at(&point_layer->ids, (size_t) point_layer->selection);
505 const char *text = edit_field_as_text(&point_layer->edit_field);
506 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
508 memset(id + n, 0, ID_MAX_SIZE - n);
510 point_layer->state = POINT_LAYER_IDLE;
516 point_layer->state = POINT_LAYER_IDLE;
524 return edit_field_event(&point_layer->edit_field, event);
528 int point_layer_move_event(PointLayer *point_layer,
529 const SDL_Event *event,
530 const Camera *camera,
531 UndoHistory *undo_history)
533 trace_assert(point_layer);
535 trace_assert(camera);
536 trace_assert(point_layer->selection >= 0);
538 Vec2f *positions = (Vec2f*)point_layer->positions.data;
540 switch (event->type) {
541 case SDL_MOUSEBUTTONUP: {
542 switch (event->button.button) {
543 case SDL_BUTTON_LEFT: {
544 point_layer->state = POINT_LAYER_IDLE;
546 const float distance = vec_length(
547 vec_sub(point_layer->inter_position,
548 positions[point_layer->selection]));
550 if (distance > 1e-6) {
553 create_point_undo_context(
558 &point_layer->positions,
559 (size_t) point_layer->selection,
560 &point_layer->inter_position);
566 case SDL_MOUSEMOTION: {
567 const Uint8 *state = SDL_GetKeyboardState(NULL);
568 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
569 const Vec2f point_pos = positions[point_layer->selection];
571 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
572 point_layer->inter_position = mouse_pos;
574 const float dx = fabsf(point_pos.x - mouse_pos.x);
575 const float dy = fabsf(point_pos.y - mouse_pos.y);
578 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
580 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
590 int point_layer_recolor_event(PointLayer *point_layer,
591 const SDL_Event *event,
592 const Camera *camera,
593 UndoHistory *undo_history)
595 trace_assert(point_layer);
597 trace_assert(camera);
598 trace_assert(undo_history);
599 trace_assert(point_layer->selection >= 0);
602 if (color_picker_event(
603 &point_layer->color_picker,
611 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
613 if (!color_picker_drag(&point_layer->color_picker)) {
616 create_point_undo_context(
621 &point_layer->colors,
622 (size_t) point_layer->selection,
623 &point_layer->inter_color);
625 point_layer->state = POINT_LAYER_IDLE;
633 int point_layer_event(PointLayer *point_layer,
634 const SDL_Event *event,
635 const Camera *camera,
636 UndoHistory *undo_history)
638 trace_assert(point_layer);
640 trace_assert(camera);
641 trace_assert(undo_history);
643 switch (point_layer->state) {
644 case POINT_LAYER_IDLE:
645 return point_layer_idle_event(point_layer, event, camera, undo_history);
647 case POINT_LAYER_EDIT_ID:
648 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
650 case POINT_LAYER_MOVE:
651 return point_layer_move_event(point_layer, event, camera, undo_history);
653 case POINT_LAYER_RECOLOR:
654 return point_layer_recolor_event(point_layer, event, camera, undo_history);
660 size_t point_layer_count(const PointLayer *point_layer)
662 trace_assert(point_layer);
663 return point_layer->positions.count;
666 const Vec2f *point_layer_positions(const PointLayer *point_layer)
668 trace_assert(point_layer);
669 return (const Vec2f *)point_layer->positions.data;
672 const Color *point_layer_colors(const PointLayer *point_layer)
674 trace_assert(point_layer);
675 return (const Color *)point_layer->colors.data;
678 const char *point_layer_ids(const PointLayer *point_layer)
680 trace_assert(point_layer);
681 return (const char *)point_layer->ids.data;
684 int point_layer_dump_stream(const PointLayer *point_layer,
687 trace_assert(point_layer);
688 trace_assert(filedump);
690 size_t n = point_layer->ids.count;
691 char *ids = (char *) point_layer->ids.data;
692 Vec2f *positions = (Vec2f *) point_layer->positions.data;
693 Color *colors = (Color *) point_layer->colors.data;
695 fprintf(filedump, "%zd\n", n);
696 for (size_t i = 0; i < n; ++i) {
697 fprintf(filedump, "%s %f %f ",
698 ids + ID_MAX_SIZE * i,
699 positions[i].x, positions[i].y);
700 color_hex_to_stream(colors[i], filedump);
701 fprintf(filedump, "\n");