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);
264 point_layer_pop_element,
273 char id[ID_MAX_SIZE];
278 DeleteContext create_delete_context(const PointLayer *point_layer,
281 DeleteContext delete_context = {
282 .position = *((Point *)dynarray_pointer_at(point_layer->positions, i)),
283 .color = *((Color *)dynarray_pointer_at(point_layer->colors, i)),
286 memcpy(delete_context.id, dynarray_pointer_at(point_layer->ids, i), ID_MAX_SIZE);
287 return delete_context;
291 void point_layer_revert_delete(void *layer, Context context)
294 PointLayer *point_layer = layer;
296 trace_assert(sizeof(DeleteContext) <= CONTEXT_SIZE);
297 DeleteContext *delete_context = (DeleteContext *)context.data;
299 dynarray_insert_before(point_layer->positions, delete_context->index, &delete_context->position);
300 dynarray_insert_before(point_layer->colors, delete_context->index, &delete_context->color);
301 dynarray_insert_before(point_layer->ids, delete_context->index, delete_context->id);
305 void point_layer_delete_nth_element(PointLayer *point_layer,
307 UndoHistory *undo_history)
309 trace_assert(point_layer);
311 DeleteContext context = create_delete_context(point_layer, i);
316 point_layer_revert_delete,
317 &context, sizeof(context)));
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)) {
371 ColorContext context = {
372 .index = (size_t) point_layer->selected,
373 .color = point_layer->prev_color
380 point_layer_revert_color,
381 &context, sizeof(context)));
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 RenameContext create_rename_context(PointLayer *point_layer, size_t index)
461 RenameContext context = {
465 memcpy(context.id, dynarray_pointer_at(point_layer->ids, index), ID_MAX_SIZE);
471 void point_layer_revert_rename(void *layer,
475 PointLayer *point_layer = layer;
477 ASSERT_CONTEXT_SIZE(RenameContext);
478 RenameContext *rename_context = (RenameContext *)context.data;
480 trace_assert(rename_context->index < dynarray_count(point_layer->ids));
482 char *ids = dynarray_data(point_layer->ids);
484 ids + rename_context->index * ID_MAX_SIZE,
490 int point_layer_edit_id_event(PointLayer *point_layer,
491 const SDL_Event *event,
492 const Camera *camera,
493 UndoHistory *undo_history)
495 trace_assert(point_layer);
497 trace_assert(camera);
499 switch (event->type) {
501 switch(event->key.keysym.sym) {
503 RenameContext rename_context = create_rename_context(
505 (size_t) point_layer->selected);
511 point_layer_revert_rename,
513 sizeof(rename_context)));
515 char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
516 const char *text = edit_field_as_text(point_layer->edit_field);
517 memset(id, 0, ID_MAX_SIZE);
518 memcpy(id, text, min_size_t(strlen(text), ID_MAX_SIZE - 1));
520 point_layer->state = POINT_LAYER_IDLE;
526 point_layer->state = POINT_LAYER_IDLE;
534 return edit_field_event(point_layer->edit_field, event);
543 void point_layer_revert_move(void *layer, Context context)
546 PointLayer *point_layer = layer;
548 ASSERT_CONTEXT_SIZE(MoveContext);
549 MoveContext *move_context = (MoveContext *)context.data;
551 trace_assert(move_context->index < dynarray_count(point_layer->positions));
552 Point *positions = dynarray_data(point_layer->positions);
553 positions[move_context->index] = move_context->position;
557 int point_layer_move_event(PointLayer *point_layer,
558 const SDL_Event *event,
559 const Camera *camera,
560 UndoHistory *undo_history)
562 trace_assert(point_layer);
564 trace_assert(camera);
565 trace_assert(point_layer->selected >= 0);
567 switch (event->type) {
568 case SDL_MOUSEBUTTONUP: {
569 switch (event->button.button) {
570 case SDL_BUTTON_LEFT: {
571 point_layer->state = POINT_LAYER_IDLE;
573 MoveContext context = {
574 .index = (size_t) point_layer->selected,
575 .position = point_layer->prev_position
578 // TODO(#1014): just click (without moving) on the point creates an undo history entry
583 point_layer_revert_move,
590 case SDL_MOUSEMOTION: {
591 Point *positions = dynarray_data(point_layer->positions);
592 positions[point_layer->selected] =
593 camera_map_screen(camera, event->motion.x, event->motion.y);
600 int point_layer_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);
610 switch (point_layer->state) {
611 case POINT_LAYER_IDLE:
612 return point_layer_idle_event(point_layer, event, camera, undo_history);
614 case POINT_LAYER_EDIT_ID:
615 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
617 case POINT_LAYER_MOVE:
618 return point_layer_move_event(point_layer, event, camera, undo_history);
624 size_t point_layer_count(const PointLayer *point_layer)
626 trace_assert(point_layer);
627 return dynarray_count(point_layer->positions);
630 const Point *point_layer_positions(const PointLayer *point_layer)
632 trace_assert(point_layer);
633 return dynarray_data(point_layer->positions);
636 const Color *point_layer_colors(const PointLayer *point_layer)
638 trace_assert(point_layer);
639 return dynarray_data(point_layer->colors);
642 const char *point_layer_ids(const PointLayer *point_layer)
644 trace_assert(point_layer);
645 return dynarray_data(point_layer->ids);
648 int point_layer_dump_stream(const PointLayer *point_layer,
651 trace_assert(point_layer);
652 trace_assert(filedump);
654 size_t n = dynarray_count(point_layer->ids);
655 char *ids = dynarray_data(point_layer->ids);
656 Point *positions = dynarray_data(point_layer->positions);
657 Color *colors = dynarray_data(point_layer->colors);
659 fprintf(filedump, "%zd\n", n);
660 for (size_t i = 0; i < n; ++i) {
661 fprintf(filedump, "%s %f %f ",
662 ids + ID_MAX_SIZE * i,
663 positions[i].x, positions[i].y);
664 color_hex_to_stream(colors[i], filedump);
665 fprintf(filedump, "\n");