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,
367 if (point_layer->selected >= 0) {
368 Color *colors = dynarray_data(point_layer->colors);
370 if (!color_picker_drag(&point_layer->color_picker)) {
372 .layer = point_layer,
373 .revert = point_layer_revert_color
376 *((ColorContext*)action.context.data) = (ColorContext) {
377 .index = (size_t) point_layer->selected,
378 .color = point_layer->prev_color
381 undo_history_push(undo_history, action);
383 point_layer->prev_color =
384 color_picker_rgba(&point_layer->color_picker);
387 colors[point_layer->selected] =
388 color_picker_rgba(&point_layer->color_picker);
394 switch (event->type) {
395 case SDL_MOUSEBUTTONDOWN: {
396 switch (event->button.button) {
397 case SDL_BUTTON_LEFT: {
398 const Point position = camera_map_screen(camera, event->button.x, event->button.y);
400 point_layer->selected = point_layer_element_at(
401 point_layer, position);
403 if (point_layer->selected < 0) {
404 point_layer_add_element(
407 color_picker_rgba(&point_layer->color_picker),
410 Color *colors = dynarray_data(point_layer->colors);
411 Point *positions = dynarray_data(point_layer->positions);
413 point_layer->state = POINT_LAYER_MOVE;
414 point_layer->color_picker =
415 create_color_picker_from_rgba(colors[point_layer->selected]);
417 point_layer->prev_color = colors[point_layer->selected];
418 point_layer->prev_position = positions[point_layer->selected];
425 switch (event->key.keysym.sym) {
427 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
428 point_layer_delete_nth_element(
430 (size_t)point_layer->selected,
432 point_layer->selected = -1;
437 if (point_layer->selected >= 0) {
438 char *ids = dynarray_data(point_layer->ids);
439 point_layer->state = POINT_LAYER_EDIT_ID;
441 point_layer->edit_field,
442 ids + ID_MAX_SIZE * point_layer->selected);
443 SDL_StartTextInput();
455 char id[ID_MAX_SIZE];
459 void point_layer_revert_rename(void *layer,
463 PointLayer *point_layer = layer;
465 ASSERT_CONTEXT_SIZE(RenameContext);
466 RenameContext *rename_context = (RenameContext *)context.data;
468 trace_assert(rename_context->index < dynarray_count(point_layer->ids));
470 char *ids = dynarray_data(point_layer->ids);
472 ids + rename_context->index * ID_MAX_SIZE,
478 int point_layer_edit_id_event(PointLayer *point_layer,
479 const SDL_Event *event,
480 const Camera *camera,
481 UndoHistory *undo_history)
483 trace_assert(point_layer);
485 trace_assert(camera);
487 switch (event->type) {
489 switch(event->key.keysym.sym) {
491 char *ids = dynarray_data(point_layer->ids);
492 const char *text = edit_field_as_text(point_layer->edit_field);
495 .revert = point_layer_revert_rename,
499 ASSERT_CONTEXT_SIZE(RenameContext);
500 RenameContext *rename_context = (RenameContext *)action.context.data;
504 ids + point_layer->selected * ID_MAX_SIZE,
506 rename_context->index = (size_t) point_layer->selected;
508 undo_history_push(undo_history, action);
510 size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
511 memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
512 *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
514 point_layer->state = POINT_LAYER_IDLE;
520 point_layer->state = POINT_LAYER_IDLE;
528 return edit_field_event(point_layer->edit_field, event);
537 void point_layer_revert_move(void *layer, Context context)
540 PointLayer *point_layer = layer;
542 ASSERT_CONTEXT_SIZE(MoveContext);
543 MoveContext *move_context = (MoveContext *)context.data;
545 trace_assert(move_context->index < dynarray_count(point_layer->positions));
546 Point *positions = dynarray_data(point_layer->positions);
547 positions[move_context->index] = move_context->position;
551 int point_layer_move_event(PointLayer *point_layer,
552 const SDL_Event *event,
553 const Camera *camera,
554 UndoHistory *undo_history)
556 trace_assert(point_layer);
558 trace_assert(camera);
559 trace_assert(point_layer->selected >= 0);
561 switch (event->type) {
562 case SDL_MOUSEBUTTONUP: {
563 switch (event->button.button) {
564 case SDL_BUTTON_LEFT: {
565 point_layer->state = POINT_LAYER_IDLE;
568 .revert = point_layer_revert_move,
572 MoveContext *context = (MoveContext *)action.context.data;
573 ASSERT_CONTEXT_SIZE(MoveContext);
575 context->index = (size_t) point_layer->selected;
576 context->position = point_layer->prev_position;
578 // TODO(#1014): just click (without moving) on the point creates an undo history entry
579 undo_history_push(undo_history, action);
584 case SDL_MOUSEMOTION: {
585 Point *positions = dynarray_data(point_layer->positions);
586 positions[point_layer->selected] =
587 camera_map_screen(camera, event->motion.x, event->motion.y);
594 int point_layer_event(PointLayer *point_layer,
595 const SDL_Event *event,
596 const Camera *camera,
597 UndoHistory *undo_history)
599 trace_assert(point_layer);
601 trace_assert(camera);
602 trace_assert(undo_history);
604 switch (point_layer->state) {
605 case POINT_LAYER_IDLE:
606 return point_layer_idle_event(point_layer, event, camera, undo_history);
608 case POINT_LAYER_EDIT_ID:
609 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
611 case POINT_LAYER_MOVE:
612 return point_layer_move_event(point_layer, event, camera, undo_history);
618 size_t point_layer_count(const PointLayer *point_layer)
620 trace_assert(point_layer);
621 return dynarray_count(point_layer->positions);
624 const Point *point_layer_positions(const PointLayer *point_layer)
626 trace_assert(point_layer);
627 return dynarray_data(point_layer->positions);
630 const Color *point_layer_colors(const PointLayer *point_layer)
632 trace_assert(point_layer);
633 return dynarray_data(point_layer->colors);
636 const char *point_layer_ids(const PointLayer *point_layer)
638 trace_assert(point_layer);
639 return dynarray_data(point_layer->ids);
642 int point_layer_dump_stream(const PointLayer *point_layer,
645 trace_assert(point_layer);
646 trace_assert(filedump);
648 size_t n = dynarray_count(point_layer->ids);
649 char *ids = dynarray_data(point_layer->ids);
650 Point *positions = dynarray_data(point_layer->positions);
651 Color *colors = dynarray_data(point_layer->colors);
653 fprintf(filedump, "%zd\n", n);
654 for (size_t i = 0; i < n; ++i) {
655 fprintf(filedump, "%s %f %f ",
656 ids + ID_MAX_SIZE * i,
657 positions[i].x, positions[i].y);
658 color_hex_to_stream(colors[i], filedump);
659 fprintf(filedump, "\n");