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();
444 int point_layer_edit_id_event(PointLayer *point_layer,
445 const SDL_Event *event,
446 const Camera *camera,
447 UndoHistory *undo_history)
449 trace_assert(point_layer);
451 trace_assert(camera);
453 switch (event->type) {
455 switch(event->key.keysym.sym) {
457 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
459 char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
460 const char *text = edit_field_as_text(point_layer->edit_field);
461 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
463 memset(id + n, 0, ID_MAX_SIZE - n);
465 point_layer->state = POINT_LAYER_IDLE;
471 point_layer->state = POINT_LAYER_IDLE;
479 return edit_field_event(point_layer->edit_field, event);
483 int point_layer_move_event(PointLayer *point_layer,
484 const SDL_Event *event,
485 const Camera *camera,
486 UndoHistory *undo_history)
488 trace_assert(point_layer);
490 trace_assert(camera);
491 trace_assert(point_layer->selected >= 0);
493 switch (event->type) {
494 case SDL_MOUSEBUTTONUP: {
495 switch (event->button.button) {
496 case SDL_BUTTON_LEFT: {
497 point_layer->state = POINT_LAYER_IDLE;
499 // TODO(#1014): just click (without moving) on the point creates an undo history entry
500 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
503 point_layer->positions,
504 (size_t) point_layer->selected,
505 &point_layer->inter_position);
510 case SDL_MOUSEMOTION: {
511 point_layer->inter_position =
512 camera_map_screen(camera, event->motion.x, event->motion.y);
520 int point_layer_recolor_event(PointLayer *point_layer,
521 const SDL_Event *event,
522 const Camera *camera,
523 UndoHistory *undo_history)
525 trace_assert(point_layer);
527 trace_assert(camera);
528 trace_assert(undo_history);
529 trace_assert(point_layer->selected >= 0);
532 if (color_picker_event(
533 &point_layer->color_picker,
541 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
543 if (!color_picker_drag(&point_layer->color_picker)) {
544 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
548 (size_t) point_layer->selected,
549 &point_layer->inter_color);
551 point_layer->state = POINT_LAYER_IDLE;
559 int point_layer_event(PointLayer *point_layer,
560 const SDL_Event *event,
561 const Camera *camera,
562 UndoHistory *undo_history)
564 trace_assert(point_layer);
566 trace_assert(camera);
567 trace_assert(undo_history);
569 switch (point_layer->state) {
570 case POINT_LAYER_IDLE:
571 return point_layer_idle_event(point_layer, event, camera, undo_history);
573 case POINT_LAYER_EDIT_ID:
574 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
576 case POINT_LAYER_MOVE:
577 return point_layer_move_event(point_layer, event, camera, undo_history);
579 case POINT_LAYER_RECOLOR:
580 return point_layer_recolor_event(point_layer, event, camera, undo_history);
586 size_t point_layer_count(const PointLayer *point_layer)
588 trace_assert(point_layer);
589 return dynarray_count(point_layer->positions);
592 const Point *point_layer_positions(const PointLayer *point_layer)
594 trace_assert(point_layer);
595 return dynarray_data(point_layer->positions);
598 const Color *point_layer_colors(const PointLayer *point_layer)
600 trace_assert(point_layer);
601 return dynarray_data(point_layer->colors);
604 const char *point_layer_ids(const PointLayer *point_layer)
606 trace_assert(point_layer);
607 return dynarray_data(point_layer->ids);
610 int point_layer_dump_stream(const PointLayer *point_layer,
613 trace_assert(point_layer);
614 trace_assert(filedump);
616 size_t n = dynarray_count(point_layer->ids);
617 char *ids = dynarray_data(point_layer->ids);
618 Point *positions = dynarray_data(point_layer->positions);
619 Color *colors = dynarray_data(point_layer->colors);
621 fprintf(filedump, "%zd\n", n);
622 for (size_t i = 0; i < n; ++i) {
623 fprintf(filedump, "%s %f %f ",
624 ids + ID_MAX_SIZE * i,
625 positions[i].x, positions[i].y);
626 color_hex_to_stream(colors[i], filedump);
627 fprintf(filedump, "\n");