6 #include "game/camera.h"
7 #include "system/line_stream.h"
8 #include "system/log.h"
10 #include "system/nth_alloc.h"
11 #include "system/stacktrace.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14 #include "./point_layer.h"
15 #include "math/extrema.h"
16 #include "math/mat3x3.h"
17 #include "./color_picker.h"
18 #include "undo_history.h"
20 #define POINT_LAYER_ELEMENT_RADIUS 10.0f
21 #define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
22 #define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
34 PointLayerState state;
35 Dynarray/*<Point>*/ *positions;
36 Dynarray/*<Color>*/ *colors;
37 Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
39 ColorPicker color_picker;
43 Edit_field *edit_field;
62 UndoContext point_layer_create_undo_context(PointLayer *point_layer,
65 UndoContext undo_context;
69 ? dynarray_count(point_layer->positions) - 1
70 : (size_t) point_layer->selected;
72 undo_context.type = type;
73 undo_context.layer = point_layer;
74 dynarray_copy_to(point_layer->positions, &undo_context.position, index);
75 dynarray_copy_to(point_layer->colors, &undo_context.color, index);
76 dynarray_copy_to(point_layer->ids, &undo_context.id, index);
77 undo_context.index = index;
83 void point_layer_undo(void *context, size_t context_size)
85 trace_assert(context);
86 trace_assert(sizeof(UndoContext) == context_size);
88 UndoContext *undo_context = context;
89 PointLayer *point_layer = undo_context->layer;
91 switch (undo_context->type) {
93 dynarray_pop(point_layer->positions, NULL);
94 dynarray_pop(point_layer->colors, NULL);
95 dynarray_pop(point_layer->ids, NULL);
96 point_layer->selected = -1;
100 dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position);
101 dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color);
102 dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id);
103 point_layer->selected = -1;
107 dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position);
108 dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color);
109 dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id);
114 #define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE) \
116 UndoContext context = point_layer_create_undo_context(LAYER, UNDO_TYPE); \
124 LayerPtr point_layer_as_layer(PointLayer *point_layer)
133 PointLayer *create_point_layer(void)
135 Lt *lt = create_lt();
137 PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
138 if (point_layer == NULL) {
141 point_layer->lt = lt;
143 point_layer->state = POINT_LAYER_IDLE;
145 point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
146 if (point_layer->positions == NULL) {
150 point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
151 if (point_layer->colors == NULL) {
155 point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
156 if (point_layer->ids == NULL) {
160 point_layer->edit_field = PUSH_LT(
163 POINT_LAYER_ID_TEXT_SIZE,
164 POINT_LAYER_ID_TEXT_COLOR),
166 if (point_layer->edit_field == NULL) {
173 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
175 trace_assert(line_stream);
177 PointLayer *point_layer = create_point_layer();
181 line_stream_next(line_stream),
184 log_fail("Could not read amount of points");
185 RETURN_LT(point_layer->lt, NULL);
189 char id[ID_MAX_SIZE];
191 for (size_t i = 0; i < count; ++i) {
193 line_stream_next(line_stream),
194 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
195 id, &x, &y, color_name) < 0) {
196 log_fail("Could not read %dth goal\n", i);
197 RETURN_LT(point_layer->lt, NULL);
199 const Color color = hexstr(color_name);
200 const Point point = vec(x, y);
202 dynarray_push(point_layer->colors, &color);
203 dynarray_push(point_layer->positions, &point);
204 dynarray_push(point_layer->ids, id);
207 point_layer->selected = -1;
209 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
214 void destroy_point_layer(PointLayer *point_layer)
216 trace_assert(point_layer);
217 RETURN_LT0(point_layer->lt);
221 Triangle element_shape(Point position, float scale)
223 return triangle_mat3x3_product(
224 equilateral_triangle(),
226 trans_mat_vec(position),
230 int point_layer_render(const PointLayer *point_layer,
234 trace_assert(point_layer);
235 trace_assert(camera);
237 const int n = (int) dynarray_count(point_layer->positions);
238 Point *positions = dynarray_data(point_layer->positions);
239 Color *colors = dynarray_data(point_layer->colors);
240 char *ids = dynarray_data(point_layer->ids);
242 for (int i = 0; i < n; ++i) {
243 const Color color = color_scale(
244 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selected
245 ? point_layer->inter_color
247 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
249 const Point position =
250 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selected
251 ? point_layer->inter_position
255 if (active && i == point_layer->selected) {
256 if (camera_fill_triangle(
260 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
261 color_invert(color)) < 0) {
265 if (point_layer->state != POINT_LAYER_EDIT_ID &&
268 ids + ID_MAX_SIZE * i,
269 POINT_LAYER_ID_TEXT_SIZE,
270 POINT_LAYER_ID_TEXT_COLOR,
276 if (camera_fill_triangle(
280 POINT_LAYER_ELEMENT_RADIUS),
286 if (point_layer->state == POINT_LAYER_EDIT_ID) {
287 if (edit_field_render_world(
288 point_layer->edit_field,
290 positions[point_layer->selected]) < 0) {
295 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
304 int point_layer_element_at(const PointLayer *point_layer,
307 trace_assert(point_layer);
309 int n = (int) dynarray_count(point_layer->positions);
310 Point *positions = dynarray_data(point_layer->positions);
312 for (int i = 0; i < n; ++i) {
313 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
322 int point_layer_add_element(PointLayer *point_layer,
325 UndoHistory *undo_history)
327 trace_assert(point_layer);
328 trace_assert(undo_history);
330 char id[ID_MAX_SIZE];
331 for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
332 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
334 id[ID_MAX_SIZE - 1] = '\0';
336 dynarray_push(point_layer->positions, &position);
337 dynarray_push(point_layer->colors, &color);
338 dynarray_push(point_layer->ids, id);
340 UNDO_PUSH(point_layer, undo_history, UNDO_ADD);
346 void point_layer_delete_nth_element(PointLayer *point_layer,
348 UndoHistory *undo_history)
350 trace_assert(point_layer);
352 UNDO_PUSH(point_layer, undo_history, UNDO_DELETE);
354 dynarray_delete_at(point_layer->positions, i);
355 dynarray_delete_at(point_layer->colors, i);
356 dynarray_delete_at(point_layer->ids, i);
360 int point_layer_idle_event(PointLayer *point_layer,
361 const SDL_Event *event,
362 const Camera *camera,
363 UndoHistory *undo_history)
365 trace_assert(point_layer);
367 trace_assert(camera);
370 if (color_picker_event(
371 &point_layer->color_picker,
379 if (point_layer->selected >= 0) {
380 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
381 point_layer->state = POINT_LAYER_RECOLOR;
386 switch (event->type) {
387 case SDL_MOUSEBUTTONDOWN: {
388 switch (event->button.button) {
389 case SDL_BUTTON_LEFT: {
390 const Point position = camera_map_screen(camera, event->button.x, event->button.y);
392 point_layer->selected = point_layer_element_at(
393 point_layer, position);
395 if (point_layer->selected < 0) {
396 point_layer_add_element(
399 color_picker_rgba(&point_layer->color_picker),
402 Color *colors = dynarray_data(point_layer->colors);
403 Point *positions = dynarray_data(point_layer->positions);
405 point_layer->state = POINT_LAYER_MOVE;
406 point_layer->color_picker =
407 create_color_picker_from_rgba(colors[point_layer->selected]);
408 point_layer->inter_position = positions[point_layer->selected];
415 switch (event->key.keysym.sym) {
417 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
418 point_layer_delete_nth_element(
420 (size_t)point_layer->selected,
422 point_layer->selected = -1;
427 if (point_layer->selected >= 0) {
428 char *ids = dynarray_data(point_layer->ids);
429 point_layer->state = POINT_LAYER_EDIT_ID;
431 point_layer->edit_field,
432 ids + ID_MAX_SIZE * point_layer->selected);
433 SDL_StartTextInput();
438 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selected >= 0) {
439 Color *colors = dynarray_data(point_layer->colors);
440 #define COLOR_BUFFER_SIZE 32
441 char buffer[COLOR_BUFFER_SIZE];
443 color_hex_to_string(colors[point_layer->selected], buffer + 1, COLOR_BUFFER_SIZE - 1);
444 SDL_SetClipboardText(buffer);
445 #undef COLOR_BUFFER_SIZE
450 if ((event->key.keysym.mod & KMOD_LCTRL) && SDL_HasClipboardText()) {
451 const char *hex = SDL_GetClipboardText();
452 if (strlen(hex) == 7 && hex[0] == '#') {
453 Color color = hexstr(hex + 1);
455 SDL_GetMouseState(&x, &y);
456 Point position = camera_map_screen(camera, x, y);
457 point_layer_add_element(
473 int point_layer_edit_id_event(PointLayer *point_layer,
474 const SDL_Event *event,
475 const Camera *camera,
476 UndoHistory *undo_history)
478 trace_assert(point_layer);
480 trace_assert(camera);
482 switch (event->type) {
484 switch(event->key.keysym.sym) {
486 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
488 char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
489 const char *text = edit_field_as_text(point_layer->edit_field);
490 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
492 memset(id + n, 0, ID_MAX_SIZE - n);
494 point_layer->state = POINT_LAYER_IDLE;
500 point_layer->state = POINT_LAYER_IDLE;
508 return edit_field_event(point_layer->edit_field, event);
512 int point_layer_move_event(PointLayer *point_layer,
513 const SDL_Event *event,
514 const Camera *camera,
515 UndoHistory *undo_history)
517 trace_assert(point_layer);
519 trace_assert(camera);
520 trace_assert(point_layer->selected >= 0);
522 switch (event->type) {
523 case SDL_MOUSEBUTTONUP: {
524 switch (event->button.button) {
525 case SDL_BUTTON_LEFT: {
526 point_layer->state = POINT_LAYER_IDLE;
528 // TODO(#1014): just click (without moving) on the point creates an undo history entry
529 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
532 point_layer->positions,
533 (size_t) point_layer->selected,
534 &point_layer->inter_position);
539 case SDL_MOUSEMOTION: {
540 point_layer->inter_position =
541 camera_map_screen(camera, event->motion.x, event->motion.y);
549 int point_layer_recolor_event(PointLayer *point_layer,
550 const SDL_Event *event,
551 const Camera *camera,
552 UndoHistory *undo_history)
554 trace_assert(point_layer);
556 trace_assert(camera);
557 trace_assert(undo_history);
558 trace_assert(point_layer->selected >= 0);
561 if (color_picker_event(
562 &point_layer->color_picker,
570 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
572 if (!color_picker_drag(&point_layer->color_picker)) {
573 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
577 (size_t) point_layer->selected,
578 &point_layer->inter_color);
580 point_layer->state = POINT_LAYER_IDLE;
588 int point_layer_event(PointLayer *point_layer,
589 const SDL_Event *event,
590 const Camera *camera,
591 UndoHistory *undo_history)
593 trace_assert(point_layer);
595 trace_assert(camera);
596 trace_assert(undo_history);
598 switch (point_layer->state) {
599 case POINT_LAYER_IDLE:
600 return point_layer_idle_event(point_layer, event, camera, undo_history);
602 case POINT_LAYER_EDIT_ID:
603 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
605 case POINT_LAYER_MOVE:
606 return point_layer_move_event(point_layer, event, camera, undo_history);
608 case POINT_LAYER_RECOLOR:
609 return point_layer_recolor_event(point_layer, event, camera, undo_history);
615 size_t point_layer_count(const PointLayer *point_layer)
617 trace_assert(point_layer);
618 return dynarray_count(point_layer->positions);
621 const Point *point_layer_positions(const PointLayer *point_layer)
623 trace_assert(point_layer);
624 return dynarray_data(point_layer->positions);
627 const Color *point_layer_colors(const PointLayer *point_layer)
629 trace_assert(point_layer);
630 return dynarray_data(point_layer->colors);
633 const char *point_layer_ids(const PointLayer *point_layer)
635 trace_assert(point_layer);
636 return dynarray_data(point_layer->ids);
639 int point_layer_dump_stream(const PointLayer *point_layer,
642 trace_assert(point_layer);
643 trace_assert(filedump);
645 size_t n = dynarray_count(point_layer->ids);
646 char *ids = dynarray_data(point_layer->ids);
647 Point *positions = dynarray_data(point_layer->positions);
648 Color *colors = dynarray_data(point_layer->colors);
650 fprintf(filedump, "%zd\n", n);
651 for (size_t i = 0; i < n; ++i) {
652 fprintf(filedump, "%s %f %f ",
653 ids + ID_MAX_SIZE * i,
654 positions[i].x, positions[i].y);
655 color_hex_to_stream(colors[i], filedump);
656 fprintf(filedump, "\n");