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 "./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 // TODO(#1002): PointLayer does not fully support UndoHistory
34 PointLayerState state;
35 Dynarray/*<Point>*/ *positions;
36 Dynarray/*<Color>*/ *colors;
37 Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
38 Edit_field *edit_field;
40 ColorPicker color_picker;
45 LayerPtr point_layer_as_layer(PointLayer *point_layer)
54 PointLayer *create_point_layer(void)
58 PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
59 if (point_layer == NULL) {
64 point_layer->state = POINT_LAYER_IDLE;
66 point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
67 if (point_layer->positions == NULL) {
71 point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
72 if (point_layer->colors == NULL) {
76 point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
77 if (point_layer->ids == NULL) {
81 point_layer->edit_field = PUSH_LT(
84 POINT_LAYER_ID_TEXT_SIZE,
85 POINT_LAYER_ID_TEXT_COLOR),
87 if (point_layer->edit_field == NULL) {
94 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
96 trace_assert(line_stream);
98 PointLayer *point_layer = create_point_layer();
102 line_stream_next(line_stream),
105 log_fail("Could not read amount of points");
106 RETURN_LT(point_layer->lt, NULL);
110 char id[ID_MAX_SIZE];
112 for (size_t i = 0; i < count; ++i) {
114 line_stream_next(line_stream),
115 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
116 id, &x, &y, color_name) < 0) {
117 log_fail("Could not read %dth goal\n", i);
118 RETURN_LT(point_layer->lt, NULL);
120 const Color color = hexstr(color_name);
121 const Point point = vec(x, y);
123 dynarray_push(point_layer->colors, &color);
124 dynarray_push(point_layer->positions, &point);
125 dynarray_push(point_layer->ids, id);
128 point_layer->selected = -1;
130 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
131 point_layer->prev_color = COLOR_RED;
136 void destroy_point_layer(PointLayer *point_layer)
138 trace_assert(point_layer);
139 RETURN_LT0(point_layer->lt);
142 int point_layer_render(const PointLayer *point_layer,
146 trace_assert(point_layer);
147 trace_assert(camera);
149 const int n = (int) dynarray_count(point_layer->positions);
150 Point *positions = dynarray_data(point_layer->positions);
151 Color *colors = dynarray_data(point_layer->colors);
152 char *ids = dynarray_data(point_layer->ids);
154 for (int i = 0; i < n; ++i) {
155 const Triangle t = triangle_mat3x3_product(
156 equilateral_triangle(),
158 trans_mat(positions[i].x, positions[i].y),
159 scale_mat(POINT_LAYER_ELEMENT_RADIUS)));
161 const Color color = color_scale(
163 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
165 if (i == point_layer->selected) {
166 const Triangle t0 = triangle_mat3x3_product(
167 equilateral_triangle(),
169 trans_mat(positions[i].x, positions[i].y),
172 if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) {
176 if (point_layer->state != POINT_LAYER_EDIT_ID &&
179 ids + ID_MAX_SIZE * i,
180 POINT_LAYER_ID_TEXT_SIZE,
181 POINT_LAYER_ID_TEXT_COLOR,
187 if (camera_fill_triangle(camera, t, color) < 0) {
193 if (point_layer->state == POINT_LAYER_EDIT_ID) {
194 if (edit_field_render_world(
195 point_layer->edit_field,
197 positions[point_layer->selected]) < 0) {
202 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
211 int point_layer_element_at(const PointLayer *point_layer,
214 trace_assert(point_layer);
216 int n = (int) dynarray_count(point_layer->positions);
217 Point *positions = dynarray_data(point_layer->positions);
219 for (int i = 0; i < n; ++i) {
220 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
229 void point_layer_pop_element(void *layer, Context context)
234 PointLayer *point_layer = layer;
236 dynarray_pop(point_layer->positions, NULL);
237 dynarray_pop(point_layer->colors, NULL);
238 dynarray_pop(point_layer->ids, NULL);
242 int point_layer_add_element(PointLayer *point_layer,
245 UndoHistory *undo_history)
247 trace_assert(point_layer);
248 trace_assert(undo_history);
250 char id[ID_MAX_SIZE];
251 for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
252 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
254 id[ID_MAX_SIZE - 1] = '\0';
256 dynarray_push(point_layer->positions, &position);
257 dynarray_push(point_layer->colors, &color);
258 dynarray_push(point_layer->ids, id);
261 .revert = point_layer_pop_element,
264 undo_history_push(undo_history, action);
272 char id[ID_MAX_SIZE];
277 void point_layer_revert_delete(void *layer, Context context)
280 PointLayer *point_layer = layer;
282 trace_assert(sizeof(DeleteContext) <= CONTEXT_SIZE);
283 DeleteContext *delete_context = (DeleteContext *)context.data;
285 dynarray_insert_before(point_layer->positions, delete_context->index, &delete_context->position);
286 dynarray_insert_before(point_layer->colors, delete_context->index, &delete_context->color);
287 dynarray_insert_before(point_layer->ids, delete_context->index, delete_context->id);
291 void point_layer_delete_nth_element(PointLayer *point_layer,
293 UndoHistory *undo_history)
295 trace_assert(point_layer);
298 .revert = point_layer_revert_delete,
302 trace_assert(sizeof(DeleteContext) <= CONTEXT_SIZE);
303 DeleteContext *delete_context = (DeleteContext *)action.context.data;
305 Point *positions = dynarray_data(point_layer->positions);
306 Color *colors = dynarray_data(point_layer->colors);
307 char *ids = dynarray_data(point_layer->ids);
309 delete_context->position = positions[i];
310 delete_context->color = colors[i];
313 ids + i * ID_MAX_SIZE,
315 delete_context->index = i;
317 undo_history_push(undo_history, action);
319 dynarray_delete_at(point_layer->positions, i);
320 dynarray_delete_at(point_layer->colors, i);
321 dynarray_delete_at(point_layer->ids, i);
330 void point_layer_revert_color(void *layer, Context context)
332 log_info("point_layer_revert_color\n");
335 PointLayer *point_layer = layer;
337 trace_assert(sizeof(ColorContext) <= CONTEXT_SIZE);
338 ColorContext *color_context = (ColorContext*)context.data;
340 const size_t n = dynarray_count(point_layer->colors);
341 Color *colors = dynarray_data(point_layer->colors);
342 trace_assert(color_context->index < n);
344 colors[color_context->index] = color_context->color;
348 int point_layer_idle_event(PointLayer *point_layer,
349 const SDL_Event *event,
350 const Camera *camera,
351 UndoHistory *undo_history)
353 trace_assert(point_layer);
355 trace_assert(camera);
358 if (color_picker_event(
359 &point_layer->color_picker,
366 if (point_layer->selected >= 0) {
367 Color *colors = dynarray_data(point_layer->colors);
369 if (!color_picker_drag(&point_layer->color_picker)) {
371 .layer = point_layer,
372 .revert = point_layer_revert_color
375 *((ColorContext*)action.context.data) = (ColorContext) {
376 .index = (size_t) point_layer->selected,
377 .color = point_layer->prev_color
380 undo_history_push(undo_history, action);
382 point_layer->prev_color =
383 color_picker_rgba(&point_layer->color_picker);
386 colors[point_layer->selected] =
387 color_picker_rgba(&point_layer->color_picker);
393 switch (event->type) {
394 case SDL_MOUSEBUTTONDOWN: {
395 switch (event->button.button) {
396 case SDL_BUTTON_LEFT: {
397 const Point position = camera_map_screen(camera, event->button.x, event->button.y);
399 point_layer->selected = point_layer_element_at(
400 point_layer, position);
402 if (point_layer->selected < 0) {
403 point_layer_add_element(
406 color_picker_rgba(&point_layer->color_picker),
409 Color *colors = dynarray_data(point_layer->colors);
410 Point *positions = dynarray_data(point_layer->positions);
412 point_layer->state = POINT_LAYER_MOVE;
413 point_layer->color_picker =
414 create_color_picker_from_rgba(colors[point_layer->selected]);
416 point_layer->prev_color = colors[point_layer->selected];
417 point_layer->prev_position = positions[point_layer->selected];
424 switch (event->key.keysym.sym) {
426 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
427 point_layer_delete_nth_element(
429 (size_t)point_layer->selected,
431 point_layer->selected = -1;
436 if (point_layer->selected >= 0) {
437 char *ids = dynarray_data(point_layer->ids);
438 point_layer->state = POINT_LAYER_EDIT_ID;
440 point_layer->edit_field,
441 ids + ID_MAX_SIZE * point_layer->selected);
442 SDL_StartTextInput();
454 char id[ID_MAX_SIZE];
458 void point_layer_revert_rename(void *layer,
462 PointLayer *point_layer = layer;
464 ASSERT_CONTEXT_SIZE(RenameContext);
465 RenameContext *rename_context = (RenameContext *)context.data;
467 trace_assert(rename_context->index < dynarray_count(point_layer->ids));
469 char *ids = dynarray_data(point_layer->ids);
471 ids + rename_context->index * ID_MAX_SIZE,
477 int point_layer_edit_id_event(PointLayer *point_layer,
478 const SDL_Event *event,
479 const Camera *camera,
480 UndoHistory *undo_history)
482 trace_assert(point_layer);
484 trace_assert(camera);
486 switch (event->type) {
488 switch(event->key.keysym.sym) {
490 char *ids = dynarray_data(point_layer->ids);
491 const char *text = edit_field_as_text(point_layer->edit_field);
494 .revert = point_layer_revert_rename,
498 ASSERT_CONTEXT_SIZE(RenameContext);
499 RenameContext *rename_context = (RenameContext *)action.context.data;
503 ids + point_layer->selected * ID_MAX_SIZE,
505 rename_context->index = (size_t) point_layer->selected;
507 undo_history_push(undo_history, action);
509 size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
510 memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
511 *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
513 point_layer->state = POINT_LAYER_IDLE;
519 point_layer->state = POINT_LAYER_IDLE;
527 return edit_field_event(point_layer->edit_field, event);
536 void point_layer_revert_move(void *layer, Context context)
539 PointLayer *point_layer = layer;
541 ASSERT_CONTEXT_SIZE(MoveContext);
542 MoveContext *move_context = (MoveContext *)context.data;
544 trace_assert(move_context->index < dynarray_count(point_layer->positions));
545 Point *positions = dynarray_data(point_layer->positions);
546 positions[move_context->index] = move_context->position;
550 int point_layer_move_event(PointLayer *point_layer,
551 const SDL_Event *event,
552 const Camera *camera,
553 UndoHistory *undo_history)
555 trace_assert(point_layer);
557 trace_assert(camera);
558 trace_assert(point_layer->selected >= 0);
560 switch (event->type) {
561 case SDL_MOUSEBUTTONUP: {
562 switch (event->button.button) {
563 case SDL_BUTTON_LEFT: {
564 point_layer->state = POINT_LAYER_IDLE;
567 .revert = point_layer_revert_move,
571 MoveContext *context = (MoveContext *)action.context.data;
572 ASSERT_CONTEXT_SIZE(MoveContext);
574 context->index = (size_t) point_layer->selected;
575 context->position = point_layer->prev_position;
577 // TODO: just click (without moving) on the point creates an undo history entry
578 undo_history_push(undo_history, action);
583 case SDL_MOUSEMOTION: {
584 Point *positions = dynarray_data(point_layer->positions);
585 positions[point_layer->selected] =
586 camera_map_screen(camera, event->motion.x, event->motion.y);
593 int point_layer_event(PointLayer *point_layer,
594 const SDL_Event *event,
595 const Camera *camera,
596 UndoHistory *undo_history)
598 trace_assert(point_layer);
600 trace_assert(camera);
601 trace_assert(undo_history);
603 switch (point_layer->state) {
604 case POINT_LAYER_IDLE:
605 return point_layer_idle_event(point_layer, event, camera, undo_history);
607 case POINT_LAYER_EDIT_ID:
608 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
610 case POINT_LAYER_MOVE:
611 return point_layer_move_event(point_layer, event, camera, undo_history);
617 size_t point_layer_count(const PointLayer *point_layer)
619 trace_assert(point_layer);
620 return dynarray_count(point_layer->positions);
623 const Point *point_layer_positions(const PointLayer *point_layer)
625 trace_assert(point_layer);
626 return dynarray_data(point_layer->positions);
629 const Color *point_layer_colors(const PointLayer *point_layer)
631 trace_assert(point_layer);
632 return dynarray_data(point_layer->colors);
635 const char *point_layer_ids(const PointLayer *point_layer)
637 trace_assert(point_layer);
638 return dynarray_data(point_layer->ids);
641 int point_layer_dump_stream(const PointLayer *point_layer,
644 trace_assert(point_layer);
645 trace_assert(filedump);
647 size_t n = dynarray_count(point_layer->ids);
648 char *ids = dynarray_data(point_layer->ids);
649 Point *positions = dynarray_data(point_layer->positions);
650 Color *colors = dynarray_data(point_layer->colors);
652 fprintf(filedump, "%zd\n", n);
653 for (size_t i = 0; i < n; ++i) {
654 fprintf(filedump, "%s %f %f ",
655 ids + ID_MAX_SIZE * i,
656 positions[i].x, positions[i].y);
657 color_hex_to_stream(colors[i], filedump);
658 fprintf(filedump, "\n");