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 PointLayer *create_point_layer_from_memory(Memory *memory,
156 const char *id_name_prefix)
158 trace_assert(memory);
159 trace_assert(id_name_prefix);
161 PointLayer *result = memory_alloc(memory, sizeof(PointLayer));
162 memset(result, 0, sizeof(PointLayer));
163 result->state = POINT_LAYER_IDLE;
164 result->positions = create_dynarray_from_memory(memory, sizeof(Vec2f));
165 result->colors = create_dynarray_from_memory(memory, sizeof(Color));
166 result->ids = create_dynarray_from_memory(memory, sizeof(char) * ID_MAX_SIZE);
167 result->edit_field.font_size = POINT_LAYER_ID_TEXT_SIZE;
168 result->edit_field.font_color = POINT_LAYER_ID_TEXT_COLOR;
169 result->id_name_prefix = id_name_prefix;
173 void point_layer_load(PointLayer *point_layer,
177 trace_assert(point_layer);
178 trace_assert(memory);
181 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
182 char id[ENTITY_MAX_ID_SIZE];
183 for (int i = 0; i < n; ++i) {
184 String line = trim(chop_by_delim(input, '\n'));
185 String string_id = trim(chop_word(&line));
187 point.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
188 point.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
189 Color color = hexs(trim(chop_word(&line)));
191 memset(id, 0, ENTITY_MAX_ID_SIZE);
192 memcpy(id, string_id.data, min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
194 dynarray_push(&point_layer->positions, &point);
195 dynarray_push(&point_layer->colors, &color);
196 dynarray_push(&point_layer->ids, id);
201 Triangle element_shape(Vec2f position, float scale)
203 return triangle_mat3x3_product(
204 equilateral_triangle(),
206 trans_mat_vec(position),
210 int point_layer_render(const PointLayer *point_layer,
211 const Camera *camera,
214 trace_assert(point_layer);
215 trace_assert(camera);
217 const int n = (int)point_layer->positions.count;
218 Vec2f *positions = (Vec2f *)point_layer->positions.data;
219 Color *colors = (Color *)point_layer->colors.data;
220 char *ids = (char *)point_layer->ids.data;
222 for (int i = 0; i < n; ++i) {
223 const Color color = color_scale(
224 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selection
225 ? point_layer->inter_color
227 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
229 const Vec2f position =
230 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selection
231 ? point_layer->inter_position
235 if (active && i == point_layer->selection) {
236 if (camera_fill_triangle(
240 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
241 color_invert(color)) < 0) {
245 if (point_layer->state != POINT_LAYER_EDIT_ID &&
248 ids + ID_MAX_SIZE * i,
249 POINT_LAYER_ID_TEXT_SIZE,
250 POINT_LAYER_ID_TEXT_COLOR,
256 if (camera_fill_triangle(
260 POINT_LAYER_ELEMENT_RADIUS),
266 if (point_layer->state == POINT_LAYER_EDIT_ID) {
267 if (edit_field_render_world(
268 &point_layer->edit_field,
270 positions[point_layer->selection]) < 0) {
275 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
284 int point_layer_element_at(const PointLayer *point_layer,
287 trace_assert(point_layer);
289 int n = (int) point_layer->positions.count;
290 Vec2f *positions = (Vec2f *)point_layer->positions.data;
292 for (int i = n - 1; i >= 0; --i) {
293 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
302 int point_layer_add_element(PointLayer *point_layer,
305 UndoHistory *undo_history)
307 trace_assert(point_layer);
308 trace_assert(undo_history);
310 char id[ID_MAX_SIZE];
311 snprintf(id, ID_MAX_SIZE, "%s_%d",
312 point_layer->id_name_prefix,
313 point_layer->id_name_counter++);
315 dynarray_push(&point_layer->positions, &position);
316 dynarray_push(&point_layer->colors, &color);
317 dynarray_push(&point_layer->ids, id);
321 create_point_undo_context(point_layer, POINT_UNDO_ADD));
327 void point_layer_swap_elements(PointLayer *point_layer,
329 UndoHistory *undo_history)
331 trace_assert(point_layer);
332 trace_assert(undo_history);
333 trace_assert(a < point_layer->positions.count);
334 trace_assert(b < point_layer->positions.count);
336 dynarray_swap(&point_layer->positions, a, b);
337 dynarray_swap(&point_layer->colors, a, b);
338 dynarray_swap(&point_layer->ids, a, b);
342 create_point_undo_swap_context(point_layer, a, b));
346 void point_layer_delete_nth_element(PointLayer *point_layer,
348 UndoHistory *undo_history)
350 trace_assert(point_layer);
354 create_point_undo_context(
358 dynarray_delete_at(&point_layer->positions, i);
359 dynarray_delete_at(&point_layer->colors, i);
360 dynarray_delete_at(&point_layer->ids, i);
364 int point_layer_idle_event(PointLayer *point_layer,
365 const SDL_Event *event,
366 const Camera *camera,
367 UndoHistory *undo_history)
369 trace_assert(point_layer);
371 trace_assert(camera);
374 if (color_picker_event(
375 &point_layer->color_picker,
383 if (point_layer->selection >= 0) {
384 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
385 point_layer->state = POINT_LAYER_RECOLOR;
390 switch (event->type) {
391 case SDL_MOUSEBUTTONDOWN: {
392 switch (event->button.button) {
393 case SDL_BUTTON_LEFT: {
394 const Vec2f position = camera_map_screen(camera, event->button.x, event->button.y);
396 point_layer->selection = point_layer_element_at(
397 point_layer, position);
399 if (point_layer->selection < 0) {
400 point_layer_add_element(
403 color_picker_rgba(&point_layer->color_picker),
406 Color *colors = (Color*)point_layer->colors.data;
407 Vec2f *positions = (Vec2f*)point_layer->positions.data;
409 point_layer->state = POINT_LAYER_MOVE;
410 point_layer->color_picker =
411 create_color_picker_from_rgba(colors[point_layer->selection]);
412 point_layer->inter_position = positions[point_layer->selection];
419 switch (event->key.keysym.sym) {
421 if ((event->key.keysym.mod & KMOD_SHIFT)
422 && (point_layer->selection >= 0)
423 && ((size_t)(point_layer->selection + 1) < 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 ((event->key.keysym.mod & KMOD_SHIFT)
435 && (point_layer->selection > 0)
436 && ((size_t) point_layer->selection < point_layer->positions.count)) {
437 point_layer_swap_elements(
439 (size_t) point_layer->selection,
440 (size_t) point_layer->selection - 1,
442 point_layer->selection--;
447 if (0 <= point_layer->selection && point_layer->selection < (int) point_layer->positions.count) {
448 point_layer_delete_nth_element(
450 (size_t)point_layer->selection,
452 point_layer->selection = -1;
457 if (point_layer->selection >= 0) {
458 char *ids = (char*)point_layer->ids.data;
459 point_layer->state = POINT_LAYER_EDIT_ID;
461 &point_layer->edit_field,
462 ids + ID_MAX_SIZE * point_layer->selection);
463 SDL_StartTextInput();
468 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selection >= 0) {
470 dynarray_copy_to(&point_layer->colors, &point_clipboard_color, (size_t)point_layer->selection);
475 if ((event->key.keysym.mod & KMOD_LCTRL) && point_clipboard) {
477 SDL_GetMouseState(&x, &y);
478 Vec2f position = camera_map_screen(camera, x, y);
480 point_layer_add_element(
483 point_clipboard_color,
495 int point_layer_edit_id_event(PointLayer *point_layer,
496 const SDL_Event *event,
497 const Camera *camera,
498 UndoHistory *undo_history)
500 trace_assert(point_layer);
502 trace_assert(camera);
504 switch (event->type) {
506 switch(event->key.keysym.sym) {
510 create_point_undo_context(
514 char *id = dynarray_pointer_at(&point_layer->ids, (size_t) point_layer->selection);
515 const char *text = edit_field_as_text(&point_layer->edit_field);
516 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
518 memset(id + n, 0, ID_MAX_SIZE - n);
520 point_layer->state = POINT_LAYER_IDLE;
526 point_layer->state = POINT_LAYER_IDLE;
534 return edit_field_event(&point_layer->edit_field, event);
538 int point_layer_move_event(PointLayer *point_layer,
539 const SDL_Event *event,
540 const Camera *camera,
541 UndoHistory *undo_history)
543 trace_assert(point_layer);
545 trace_assert(camera);
546 trace_assert(point_layer->selection >= 0);
548 Vec2f *positions = (Vec2f*)point_layer->positions.data;
550 switch (event->type) {
551 case SDL_MOUSEBUTTONUP: {
552 switch (event->button.button) {
553 case SDL_BUTTON_LEFT: {
554 point_layer->state = POINT_LAYER_IDLE;
556 const float distance = vec_length(
557 vec_sub(point_layer->inter_position,
558 positions[point_layer->selection]));
560 if (distance > 1e-6) {
563 create_point_undo_context(
568 &point_layer->positions,
569 (size_t) point_layer->selection,
570 &point_layer->inter_position);
576 case SDL_MOUSEMOTION: {
577 const Uint8 *state = SDL_GetKeyboardState(NULL);
578 const Vec2f mouse_pos = camera_map_screen(camera, event->motion.x, event->motion.y);
579 const Vec2f point_pos = positions[point_layer->selection];
581 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
582 point_layer->inter_position = mouse_pos;
584 const float dx = fabsf(point_pos.x - mouse_pos.x);
585 const float dy = fabsf(point_pos.y - mouse_pos.y);
588 point_layer->inter_position = vec(mouse_pos.x, point_pos.y);
590 point_layer->inter_position = vec(point_pos.x, mouse_pos.y);
600 int point_layer_recolor_event(PointLayer *point_layer,
601 const SDL_Event *event,
602 const Camera *camera,
603 UndoHistory *undo_history)
605 trace_assert(point_layer);
607 trace_assert(camera);
608 trace_assert(undo_history);
609 trace_assert(point_layer->selection >= 0);
612 if (color_picker_event(
613 &point_layer->color_picker,
621 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
623 if (!color_picker_drag(&point_layer->color_picker)) {
626 create_point_undo_context(
631 &point_layer->colors,
632 (size_t) point_layer->selection,
633 &point_layer->inter_color);
635 point_layer->state = POINT_LAYER_IDLE;
643 int point_layer_event(PointLayer *point_layer,
644 const SDL_Event *event,
645 const Camera *camera,
646 UndoHistory *undo_history)
648 trace_assert(point_layer);
650 trace_assert(camera);
651 trace_assert(undo_history);
653 switch (point_layer->state) {
654 case POINT_LAYER_IDLE:
655 return point_layer_idle_event(point_layer, event, camera, undo_history);
657 case POINT_LAYER_EDIT_ID:
658 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
660 case POINT_LAYER_MOVE:
661 return point_layer_move_event(point_layer, event, camera, undo_history);
663 case POINT_LAYER_RECOLOR:
664 return point_layer_recolor_event(point_layer, event, camera, undo_history);
670 size_t point_layer_count(const PointLayer *point_layer)
672 trace_assert(point_layer);
673 return point_layer->positions.count;
676 const Vec2f *point_layer_positions(const PointLayer *point_layer)
678 trace_assert(point_layer);
679 return (const Vec2f *)point_layer->positions.data;
682 const Color *point_layer_colors(const PointLayer *point_layer)
684 trace_assert(point_layer);
685 return (const Color *)point_layer->colors.data;
688 const char *point_layer_ids(const PointLayer *point_layer)
690 trace_assert(point_layer);
691 return (const char *)point_layer->ids.data;
694 int point_layer_dump_stream(const PointLayer *point_layer,
697 trace_assert(point_layer);
698 trace_assert(filedump);
700 size_t n = point_layer->ids.count;
701 char *ids = (char *) point_layer->ids.data;
702 Vec2f *positions = (Vec2f *) point_layer->positions.data;
703 Color *colors = (Color *) point_layer->colors.data;
705 fprintf(filedump, "%zd\n", n);
706 for (size_t i = 0; i < n; ++i) {
707 fprintf(filedump, "%s %f %f ",
708 ids + ID_MAX_SIZE * i,
709 positions[i].x, positions[i].y);
710 color_hex_to_stream(colors[i], filedump);
711 fprintf(filedump, "\n");