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
24 static int clipboard = 0;
25 static Color clipboard_color;
37 PointLayerState state;
38 Dynarray/*<Point>*/ *positions;
39 Dynarray/*<Color>*/ *colors;
40 Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
42 ColorPicker color_picker;
46 Edit_field *edit_field;
65 UndoContext point_layer_create_undo_context(PointLayer *point_layer,
68 UndoContext undo_context;
72 ? dynarray_count(point_layer->positions) - 1
73 : (size_t) point_layer->selected;
75 undo_context.type = type;
76 undo_context.layer = point_layer;
77 dynarray_copy_to(point_layer->positions, &undo_context.position, index);
78 dynarray_copy_to(point_layer->colors, &undo_context.color, index);
79 dynarray_copy_to(point_layer->ids, &undo_context.id, index);
80 undo_context.index = index;
86 void point_layer_undo(void *context, size_t context_size)
88 trace_assert(context);
89 trace_assert(sizeof(UndoContext) == context_size);
91 UndoContext *undo_context = context;
92 PointLayer *point_layer = undo_context->layer;
94 switch (undo_context->type) {
96 dynarray_pop(point_layer->positions, NULL);
97 dynarray_pop(point_layer->colors, NULL);
98 dynarray_pop(point_layer->ids, NULL);
99 point_layer->selected = -1;
103 dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position);
104 dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color);
105 dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id);
106 point_layer->selected = -1;
110 dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position);
111 dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color);
112 dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id);
117 #define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE) \
119 UndoContext context = point_layer_create_undo_context(LAYER, UNDO_TYPE); \
127 LayerPtr point_layer_as_layer(PointLayer *point_layer)
136 PointLayer *create_point_layer(void)
138 Lt *lt = create_lt();
140 PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
141 if (point_layer == NULL) {
144 point_layer->lt = lt;
146 point_layer->state = POINT_LAYER_IDLE;
148 point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
149 if (point_layer->positions == NULL) {
153 point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
154 if (point_layer->colors == NULL) {
158 point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
159 if (point_layer->ids == NULL) {
163 point_layer->edit_field = PUSH_LT(
166 POINT_LAYER_ID_TEXT_SIZE,
167 POINT_LAYER_ID_TEXT_COLOR),
169 if (point_layer->edit_field == NULL) {
176 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
178 trace_assert(line_stream);
180 PointLayer *point_layer = create_point_layer();
184 line_stream_next(line_stream),
187 log_fail("Could not read amount of points");
188 RETURN_LT(point_layer->lt, NULL);
192 char id[ID_MAX_SIZE];
194 for (size_t i = 0; i < count; ++i) {
196 line_stream_next(line_stream),
197 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
198 id, &x, &y, color_name) < 0) {
199 log_fail("Could not read %dth goal\n", i);
200 RETURN_LT(point_layer->lt, NULL);
202 const Color color = hexstr(color_name);
203 const Point point = vec(x, y);
205 dynarray_push(point_layer->colors, &color);
206 dynarray_push(point_layer->positions, &point);
207 dynarray_push(point_layer->ids, id);
210 point_layer->selected = -1;
212 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
217 void destroy_point_layer(PointLayer *point_layer)
219 trace_assert(point_layer);
220 RETURN_LT0(point_layer->lt);
224 Triangle element_shape(Point position, float scale)
226 return triangle_mat3x3_product(
227 equilateral_triangle(),
229 trans_mat_vec(position),
233 int point_layer_render(const PointLayer *point_layer,
237 trace_assert(point_layer);
238 trace_assert(camera);
240 const int n = (int) dynarray_count(point_layer->positions);
241 Point *positions = dynarray_data(point_layer->positions);
242 Color *colors = dynarray_data(point_layer->colors);
243 char *ids = dynarray_data(point_layer->ids);
245 for (int i = 0; i < n; ++i) {
246 const Color color = color_scale(
247 point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selected
248 ? point_layer->inter_color
250 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
252 const Point position =
253 point_layer->state == POINT_LAYER_MOVE && i == point_layer->selected
254 ? point_layer->inter_position
258 if (active && i == point_layer->selected) {
259 if (camera_fill_triangle(
263 POINT_LAYER_ELEMENT_RADIUS + 5.0f),
264 color_invert(color)) < 0) {
268 if (point_layer->state != POINT_LAYER_EDIT_ID &&
271 ids + ID_MAX_SIZE * i,
272 POINT_LAYER_ID_TEXT_SIZE,
273 POINT_LAYER_ID_TEXT_COLOR,
279 if (camera_fill_triangle(
283 POINT_LAYER_ELEMENT_RADIUS),
289 if (point_layer->state == POINT_LAYER_EDIT_ID) {
290 if (edit_field_render_world(
291 point_layer->edit_field,
293 positions[point_layer->selected]) < 0) {
298 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
307 int point_layer_element_at(const PointLayer *point_layer,
310 trace_assert(point_layer);
312 int n = (int) dynarray_count(point_layer->positions);
313 Point *positions = dynarray_data(point_layer->positions);
315 for (int i = 0; i < n; ++i) {
316 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
325 int point_layer_add_element(PointLayer *point_layer,
328 UndoHistory *undo_history)
330 trace_assert(point_layer);
331 trace_assert(undo_history);
333 char id[ID_MAX_SIZE];
334 for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
335 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
337 id[ID_MAX_SIZE - 1] = '\0';
339 dynarray_push(point_layer->positions, &position);
340 dynarray_push(point_layer->colors, &color);
341 dynarray_push(point_layer->ids, id);
343 UNDO_PUSH(point_layer, undo_history, UNDO_ADD);
349 void point_layer_delete_nth_element(PointLayer *point_layer,
351 UndoHistory *undo_history)
353 trace_assert(point_layer);
355 UNDO_PUSH(point_layer, undo_history, UNDO_DELETE);
357 dynarray_delete_at(point_layer->positions, i);
358 dynarray_delete_at(point_layer->colors, i);
359 dynarray_delete_at(point_layer->ids, i);
363 int point_layer_idle_event(PointLayer *point_layer,
364 const SDL_Event *event,
365 const Camera *camera,
366 UndoHistory *undo_history)
368 trace_assert(point_layer);
370 trace_assert(camera);
373 if (color_picker_event(
374 &point_layer->color_picker,
382 if (point_layer->selected >= 0) {
383 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
384 point_layer->state = POINT_LAYER_RECOLOR;
389 switch (event->type) {
390 case SDL_MOUSEBUTTONDOWN: {
391 switch (event->button.button) {
392 case SDL_BUTTON_LEFT: {
393 const Point position = camera_map_screen(camera, event->button.x, event->button.y);
395 point_layer->selected = point_layer_element_at(
396 point_layer, position);
398 if (point_layer->selected < 0) {
399 point_layer_add_element(
402 color_picker_rgba(&point_layer->color_picker),
405 Color *colors = dynarray_data(point_layer->colors);
406 Point *positions = dynarray_data(point_layer->positions);
408 point_layer->state = POINT_LAYER_MOVE;
409 point_layer->color_picker =
410 create_color_picker_from_rgba(colors[point_layer->selected]);
411 point_layer->inter_position = positions[point_layer->selected];
418 switch (event->key.keysym.sym) {
420 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
421 point_layer_delete_nth_element(
423 (size_t)point_layer->selected,
425 point_layer->selected = -1;
430 if (point_layer->selected >= 0) {
431 char *ids = dynarray_data(point_layer->ids);
432 point_layer->state = POINT_LAYER_EDIT_ID;
434 point_layer->edit_field,
435 ids + ID_MAX_SIZE * point_layer->selected);
436 SDL_StartTextInput();
441 if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selected >= 0) {
443 dynarray_copy_to(point_layer->colors, &clipboard_color, (size_t)point_layer->selected);
448 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
450 SDL_GetMouseState(&x, &y);
451 Point position = camera_map_screen(camera, x, y);
453 point_layer_add_element(
468 int point_layer_edit_id_event(PointLayer *point_layer,
469 const SDL_Event *event,
470 const Camera *camera,
471 UndoHistory *undo_history)
473 trace_assert(point_layer);
475 trace_assert(camera);
477 switch (event->type) {
479 switch(event->key.keysym.sym) {
481 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
483 char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
484 const char *text = edit_field_as_text(point_layer->edit_field);
485 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
487 memset(id + n, 0, ID_MAX_SIZE - n);
489 point_layer->state = POINT_LAYER_IDLE;
495 point_layer->state = POINT_LAYER_IDLE;
503 return edit_field_event(point_layer->edit_field, event);
507 int point_layer_move_event(PointLayer *point_layer,
508 const SDL_Event *event,
509 const Camera *camera,
510 UndoHistory *undo_history)
512 trace_assert(point_layer);
514 trace_assert(camera);
515 trace_assert(point_layer->selected >= 0);
517 switch (event->type) {
518 case SDL_MOUSEBUTTONUP: {
519 switch (event->button.button) {
520 case SDL_BUTTON_LEFT: {
521 point_layer->state = POINT_LAYER_IDLE;
523 // TODO(#1014): just click (without moving) on the point creates an undo history entry
524 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
527 point_layer->positions,
528 (size_t) point_layer->selected,
529 &point_layer->inter_position);
534 case SDL_MOUSEMOTION: {
535 point_layer->inter_position =
536 camera_map_screen(camera, event->motion.x, event->motion.y);
544 int point_layer_recolor_event(PointLayer *point_layer,
545 const SDL_Event *event,
546 const Camera *camera,
547 UndoHistory *undo_history)
549 trace_assert(point_layer);
551 trace_assert(camera);
552 trace_assert(undo_history);
553 trace_assert(point_layer->selected >= 0);
556 if (color_picker_event(
557 &point_layer->color_picker,
565 point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
567 if (!color_picker_drag(&point_layer->color_picker)) {
568 UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
572 (size_t) point_layer->selected,
573 &point_layer->inter_color);
575 point_layer->state = POINT_LAYER_IDLE;
583 int point_layer_event(PointLayer *point_layer,
584 const SDL_Event *event,
585 const Camera *camera,
586 UndoHistory *undo_history)
588 trace_assert(point_layer);
590 trace_assert(camera);
591 trace_assert(undo_history);
593 switch (point_layer->state) {
594 case POINT_LAYER_IDLE:
595 return point_layer_idle_event(point_layer, event, camera, undo_history);
597 case POINT_LAYER_EDIT_ID:
598 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
600 case POINT_LAYER_MOVE:
601 return point_layer_move_event(point_layer, event, camera, undo_history);
603 case POINT_LAYER_RECOLOR:
604 return point_layer_recolor_event(point_layer, event, camera, undo_history);
610 size_t point_layer_count(const PointLayer *point_layer)
612 trace_assert(point_layer);
613 return dynarray_count(point_layer->positions);
616 const Point *point_layer_positions(const PointLayer *point_layer)
618 trace_assert(point_layer);
619 return dynarray_data(point_layer->positions);
622 const Color *point_layer_colors(const PointLayer *point_layer)
624 trace_assert(point_layer);
625 return dynarray_data(point_layer->colors);
628 const char *point_layer_ids(const PointLayer *point_layer)
630 trace_assert(point_layer);
631 return dynarray_data(point_layer->ids);
634 int point_layer_dump_stream(const PointLayer *point_layer,
637 trace_assert(point_layer);
638 trace_assert(filedump);
640 size_t n = dynarray_count(point_layer->ids);
641 char *ids = dynarray_data(point_layer->ids);
642 Point *positions = dynarray_data(point_layer->positions);
643 Color *colors = dynarray_data(point_layer->colors);
645 fprintf(filedump, "%zd\n", n);
646 for (size_t i = 0; i < n; ++i) {
647 fprintf(filedump, "%s %f %f ",
648 ids + ID_MAX_SIZE * i,
649 positions[i].x, positions[i].y);
650 color_hex_to_stream(colors[i], filedump);
651 fprintf(filedump, "\n");