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_from_memory(Memory *memory,
143 const char *id_name_prefix)
145 trace_assert(memory);
146 trace_assert(id_name_prefix);
148 PointLayer *result = memory_alloc(memory, sizeof(PointLayer));
149 memset(result, 0, sizeof(PointLayer));
150 result->state = POINT_LAYER_IDLE;
151 result->positions = create_dynarray(memory, sizeof(Vec2f));
152 result->colors = create_dynarray(memory, sizeof(Color));
153 result->ids = create_dynarray(memory, sizeof(char) * ID_MAX_SIZE);
154 result->edit_field.font_size = POINT_LAYER_ID_TEXT_SIZE;
155 result->edit_field.font_color = POINT_LAYER_ID_TEXT_COLOR;
156 result->id_name_prefix = id_name_prefix;
160 void point_layer_load(PointLayer *point_layer,
164 trace_assert(point_layer);
165 trace_assert(memory);
168 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
169 char id[ENTITY_MAX_ID_SIZE];
170 for (int i = 0; i < n; ++i) {
171 String line = trim(chop_by_delim(input, '\n'));
172 String string_id = trim(chop_word(&line));
174 point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
175 point.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
176 Color color = hexs(trim(chop_word(&line)));
178 memset(id, 0, ENTITY_MAX_ID_SIZE);
179 memcpy(id, string_id.data, min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
181 dynarray_push(&point_layer->positions, &point);
182 dynarray_push(&point_layer->colors, &color);
183 dynarray_push(&point_layer->ids, id);
188 Triangle element_shape(Vec2f position, float scale)
190 return triangle_mat3x3_product(
191 equilateral_triangle(),
193 trans_mat_vec(position),
197 int point_layer_render(const PointLayer *point_layer,
198 const Camera *camera,
201 trace_assert(point_layer);
202 trace_assert(camera);
204 const int n = (int)point_layer->positions.count;
205 Vec2f *positions = (Vec2f *)point_layer->positions.data;
206 Color *colors = (Color *)point_layer->colors.data;
207 char *ids = (char *)point_layer->ids.data;
209 for (int i = 0; i < n; ++i) {
210 const Color color = color_scale(
211 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
212 ? point_layer->inter_color
214 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
216 const Vec2f position =
217 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
218 ? point_layer->inter_position
222 if (active && i == point_layer->selection) {
223 if (camera_fill_triangle(
227 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
228 color_invert(color)) < 0) {
232 if (point_layer->state != POINT_LAYER_EDIT_ID &&
235 ids + ID_MAX_SIZE * i,
236 POINT_LAYER_ID_TEXT_SIZE,
237 POINT_LAYER_ID_TEXT_COLOR,
243 if (camera_fill_triangle(
247 POINT_LAYER_ELEMENT_RADIUS),
253 if (point_layer->state == POINT_LAYER_EDIT_ID) {
254 if (edit_field_render_world(
255 &point_layer->edit_field,
257 positions[point_layer->selection]) < 0) {
262 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
271 int point_layer_element_at(const PointLayer *point_layer,
274 trace_assert(point_layer);
276 int n = (int) point_layer->positions.count;
277 Vec2f *positions = (Vec2f *)point_layer->positions.data;
279 for (int i = n - 1; i >= 0; --i) {
280 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
289 int point_layer_add_element(PointLayer *point_layer,
292 UndoHistory *undo_history)
294 trace_assert(point_layer);
295 trace_assert(undo_history);
297 char id[ID_MAX_SIZE];
298 snprintf(id, ID_MAX_SIZE, "%s_%d",
299 point_layer->id_name_prefix,
300 point_layer->id_name_counter++);
302 dynarray_push(&point_layer->positions, &position);
303 dynarray_push(&point_layer->colors, &color);
304 dynarray_push(&point_layer->ids, id);
308 create_point_undo_context(point_layer, POINT_UNDO_ADD));
314 void point_layer_swap_elements(PointLayer *point_layer,
316 UndoHistory *undo_history)
318 trace_assert(point_layer);
319 trace_assert(undo_history);
320 trace_assert(a < point_layer->positions.count);
321 trace_assert(b < point_layer->positions.count);
323 dynarray_swap(&point_layer->positions, a, b);
324 dynarray_swap(&point_layer->colors, a, b);
325 dynarray_swap(&point_layer->ids, a, b);
329 create_point_undo_swap_context(point_layer, a, b));
333 void point_layer_delete_nth_element(PointLayer *point_layer,
335 UndoHistory *undo_history)
337 trace_assert(point_layer);
341 create_point_undo_context(
345 dynarray_delete_at(&point_layer->positions, i);
346 dynarray_delete_at(&point_layer->colors, i);
347 dynarray_delete_at(&point_layer->ids, i);
351 int point_layer_idle_event(PointLayer *point_layer,
352 const SDL_Event *event,
353 const Camera *camera,
354 UndoHistory *undo_history)
356 trace_assert(point_layer);
358 trace_assert(camera);
361 if (color_picker_event(
362 &point_layer->color_picker,
370 if (point_layer->selection >= 0) {
371 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
372 point_layer->state = POINT_LAYER_RECOLOR;
377 switch (event->type) {
378 case SDL_MOUSEBUTTONDOWN: {
379 switch (event->button.button) {
380 case SDL_BUTTON_LEFT: {
381 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
383 point_layer->selection = point_layer_element_at(
384 point_layer, position);
386 if (point_layer->selection < 0) {
387 point_layer_add_element(
390 color_picker_rgba(&point_layer->color_picker),
393 Color *colors = (Color*)point_layer->colors.data;
394 Vec2f *positions = (Vec2f*)point_layer->positions.data;
396 point_layer->state = POINT_LAYER_MOVE;
397 point_layer->color_picker =
398 create_color_picker_from_rgba(colors[point_layer->selection]);
399 point_layer->inter_position = positions[point_layer->selection];
406 switch (event->key.keysym.sym) {
408 if ((event->key.keysym.mod & KMOD_SHIFT)
409 && (point_layer->selection >= 0)
410 && ((size_t)(point_layer->selection + 1) < point_layer->positions.count)) {
411 point_layer_swap_elements(
413 (size_t) point_layer->selection,
414 (size_t) point_layer->selection + 1,
416 point_layer->selection++;
421 if ((event->key.keysym.mod & KMOD_SHIFT)
422 && (point_layer->selection > 0)
423 && ((size_t) point_layer->selection < point_layer->positions.count)) {
424 point_layer_swap_elements(
426 (size_t) point_layer->selection,
427 (size_t) point_layer->selection - 1,
429 point_layer->selection--;
434 if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
435 point_layer_delete_nth_element(
437 (size_t)point_layer->selection,
439 point_layer->selection = -1;
444 if (point_layer->selection >= 0) {
445 char *ids = (char*)point_layer->ids.data;
446 point_layer->state = POINT_LAYER_EDIT_ID;
448 &point_layer->edit_field,
449 ids + ID_MAX_SIZE * point_layer->selection);
450 SDL_StartTextInput();
455 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
457 dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
462 if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
464 SDL_GetMouseState(&x, &y);
465 Vec2f position = camera_map_screen(camera, x, y);
467 point_layer_add_element(
470 point_clipboard_color,
482 int point_layer_edit_id_event(PointLayer *point_layer,
483 const SDL_Event *event,
484 const Camera *camera,
485 UndoHistory *undo_history)
487 trace_assert(point_layer);
489 trace_assert(camera);
491 switch (event->type) {
493 switch(event->key.keysym.sym) {
497 create_point_undo_context(
501 char *id = dynarray_pointer_at(&point_layer->ids, (size_t) point_layer->selection);
502 const char *text = edit_field_as_text(&point_layer->edit_field);
503 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
505 memset(id + n, 0, ID_MAX_SIZE - n);
507 point_layer->state = POINT_LAYER_IDLE;
513 point_layer->state = POINT_LAYER_IDLE;
521 return edit_field_event(&point_layer->edit_field, event);
525 int point_layer_move_event(PointLayer *point_layer,
526 const SDL_Event *event,
527 const Camera *camera,
528 UndoHistory *undo_history)
530 trace_assert(point_layer);
532 trace_assert(camera);
533 trace_assert(point_layer->selection >= 0);
535 Vec2f *positions = (Vec2f*)point_layer->positions.data;
537 switch (event->type) {
538 case SDL_MOUSEBUTTONUP: {
539 switch (event->button.button) {
540 case SDL_BUTTON_LEFT: {
541 point_layer->state = POINT_LAYER_IDLE;
543 const float distance = vec_length(
544 vec_sub(point_layer->inter_position,
545 positions[point_layer->selection]));
547 if (distance > 1e-6) {
550 create_point_undo_context(
555 &point_layer->positions,
556 (size_t) point_layer->selection,
557 &point_layer->inter_position);
563 case SDL_MOUSEMOTION: {
564 const Uint8 *state = SDL_GetKeyboardState(NULL);
565 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
566 const Vec2f point_pos = positions[point_layer->selection];
568 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
569 point_layer->inter_position = mouse_pos;
571 const float dx = fabsf(point_pos.x - mouse_pos.x);
572 const float dy = fabsf(point_pos.y - mouse_pos.y);
575 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
577 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
587 int point_layer_recolor_event(PointLayer *point_layer,
588 const SDL_Event *event,
589 const Camera *camera,
590 UndoHistory *undo_history)
592 trace_assert(point_layer);
594 trace_assert(camera);
595 trace_assert(undo_history);
596 trace_assert(point_layer->selection >= 0);
599 if (color_picker_event(
600 &point_layer->color_picker,
608 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
610 if (!color_picker_drag(&point_layer->color_picker)) {
613 create_point_undo_context(
618 &point_layer->colors,
619 (size_t) point_layer->selection,
620 &point_layer->inter_color);
622 point_layer->state = POINT_LAYER_IDLE;
630 int point_layer_event(PointLayer *point_layer,
631 const SDL_Event *event,
632 const Camera *camera,
633 UndoHistory *undo_history)
635 trace_assert(point_layer);
637 trace_assert(camera);
638 trace_assert(undo_history);
640 switch (point_layer->state) {
641 case POINT_LAYER_IDLE:
642 return point_layer_idle_event(point_layer, event, camera, undo_history);
644 case POINT_LAYER_EDIT_ID:
645 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
647 case POINT_LAYER_MOVE:
648 return point_layer_move_event(point_layer, event, camera, undo_history);
650 case POINT_LAYER_RECOLOR:
651 return point_layer_recolor_event(point_layer, event, camera, undo_history);
657 size_t point_layer_count(const PointLayer *point_layer)
659 trace_assert(point_layer);
660 return point_layer->positions.count;
663 const Vec2f *point_layer_positions(const PointLayer *point_layer)
665 trace_assert(point_layer);
666 return (const Vec2f *)point_layer->positions.data;
669 const Color *point_layer_colors(const PointLayer *point_layer)
671 trace_assert(point_layer);
672 return (const Color *)point_layer->colors.data;
675 const char *point_layer_ids(const PointLayer *point_layer)
677 trace_assert(point_layer);
678 return (const char *)point_layer->ids.data;
681 int point_layer_dump_stream(const PointLayer *point_layer,
684 trace_assert(point_layer);
685 trace_assert(filedump);
687 size_t n = point_layer->ids.count;
688 char *ids = (char *) point_layer->ids.data;
689 Vec2f *positions = (Vec2f *) point_layer->positions.data;
690 Color *colors = (Color *) point_layer->colors.data;
692 fprintf(filedump, "%zd\n", n);
693 for (size_t i = 0; i < n; ++i) {
694 fprintf(filedump, "%s %f %f ",
695 ids + ID_MAX_SIZE * i,
696 positions[i].x, positions[i].y);
697 color_hex_to_stream(colors[i], filedump);
698 fprintf(filedump, "\n");