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
32 PointLayerState state;
33 Dynarray/*<Point>*/ *positions;
34 Dynarray/*<Color>*/ *colors;
35 Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
36 Edit_field *edit_field;
38 ColorPicker color_picker;
52 UndoContext point_layer_create_undo_context(PointLayer *point_layer,
56 UndoContext undo_context;
58 undo_context.type = type;
59 undo_context.position = point_layer->prev_position;
60 undo_context.color = point_layer->prev_color;
61 dynarray_copy_to(point_layer->ids, &undo_context.id, index);
62 undo_context.index = index;
68 void point_layer_undo(void *layer, Context context)
71 PointLayer *point_layer = layer;
73 UndoContext *undo_context = (UndoContext *)context.data;
75 switch (undo_context->type) {
77 dynarray_pop(point_layer->positions, NULL);
78 dynarray_pop(point_layer->colors, NULL);
79 dynarray_pop(point_layer->ids, NULL);
80 point_layer->selected = -1;
84 dynarray_insert_before(point_layer->positions, undo_context->index, &undo_context->position);
85 dynarray_insert_before(point_layer->colors, undo_context->index, &undo_context->color);
86 dynarray_insert_before(point_layer->ids, undo_context->index, &undo_context->id);
87 point_layer->selected = -1;
91 dynarray_replace_at(point_layer->positions, undo_context->index, &undo_context->position);
92 dynarray_replace_at(point_layer->colors, undo_context->index, &undo_context->color);
93 dynarray_replace_at(point_layer->ids, undo_context->index, &undo_context->id);
98 LayerPtr point_layer_as_layer(PointLayer *point_layer)
107 PointLayer *create_point_layer(void)
109 Lt *lt = create_lt();
111 PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
112 if (point_layer == NULL) {
115 point_layer->lt = lt;
117 point_layer->state = POINT_LAYER_IDLE;
119 point_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
120 if (point_layer->positions == NULL) {
124 point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
125 if (point_layer->colors == NULL) {
129 point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
130 if (point_layer->ids == NULL) {
134 point_layer->edit_field = PUSH_LT(
137 POINT_LAYER_ID_TEXT_SIZE,
138 POINT_LAYER_ID_TEXT_COLOR),
140 if (point_layer->edit_field == NULL) {
147 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
149 trace_assert(line_stream);
151 PointLayer *point_layer = create_point_layer();
155 line_stream_next(line_stream),
158 log_fail("Could not read amount of points");
159 RETURN_LT(point_layer->lt, NULL);
163 char id[ID_MAX_SIZE];
165 for (size_t i = 0; i < count; ++i) {
167 line_stream_next(line_stream),
168 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
169 id, &x, &y, color_name) < 0) {
170 log_fail("Could not read %dth goal\n", i);
171 RETURN_LT(point_layer->lt, NULL);
173 const Color color = hexstr(color_name);
174 const Point point = vec(x, y);
176 dynarray_push(point_layer->colors, &color);
177 dynarray_push(point_layer->positions, &point);
178 dynarray_push(point_layer->ids, id);
181 point_layer->selected = -1;
183 point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
184 point_layer->prev_color = COLOR_RED;
189 void destroy_point_layer(PointLayer *point_layer)
191 trace_assert(point_layer);
192 RETURN_LT0(point_layer->lt);
195 int point_layer_render(const PointLayer *point_layer,
199 trace_assert(point_layer);
200 trace_assert(camera);
202 const int n = (int) dynarray_count(point_layer->positions);
203 Point *positions = dynarray_data(point_layer->positions);
204 Color *colors = dynarray_data(point_layer->colors);
205 char *ids = dynarray_data(point_layer->ids);
207 for (int i = 0; i < n; ++i) {
208 const Triangle t = triangle_mat3x3_product(
209 equilateral_triangle(),
211 trans_mat(positions[i].x, positions[i].y),
212 scale_mat(POINT_LAYER_ELEMENT_RADIUS)));
214 const Color color = color_scale(
216 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
218 if (i == point_layer->selected) {
219 const Triangle t0 = triangle_mat3x3_product(
220 equilateral_triangle(),
222 trans_mat(positions[i].x, positions[i].y),
225 if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) {
229 if (point_layer->state != POINT_LAYER_EDIT_ID &&
232 ids + ID_MAX_SIZE * i,
233 POINT_LAYER_ID_TEXT_SIZE,
234 POINT_LAYER_ID_TEXT_COLOR,
240 if (camera_fill_triangle(camera, t, color) < 0) {
246 if (point_layer->state == POINT_LAYER_EDIT_ID) {
247 if (edit_field_render_world(
248 point_layer->edit_field,
250 positions[point_layer->selected]) < 0) {
255 if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
264 int point_layer_element_at(const PointLayer *point_layer,
267 trace_assert(point_layer);
269 int n = (int) dynarray_count(point_layer->positions);
270 Point *positions = dynarray_data(point_layer->positions);
272 for (int i = 0; i < n; ++i) {
273 if (vec_length(vec_sub(positions[i], position)) < POINT_LAYER_ELEMENT_RADIUS) {
282 int point_layer_add_element(PointLayer *point_layer,
285 UndoHistory *undo_history)
287 trace_assert(point_layer);
288 trace_assert(undo_history);
290 size_t index = dynarray_count(point_layer->positions);
292 char id[ID_MAX_SIZE];
293 for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
294 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
296 id[ID_MAX_SIZE - 1] = '\0';
298 dynarray_push(point_layer->positions, &position);
299 dynarray_push(point_layer->colors, &color);
300 dynarray_push(point_layer->ids, id);
302 UndoContext context =
303 point_layer_create_undo_context(point_layer, index, UNDO_ADD);
309 &context, sizeof(context));
315 void point_layer_delete_nth_element(PointLayer *point_layer,
317 UndoHistory *undo_history)
319 trace_assert(point_layer);
321 UndoContext context = point_layer_create_undo_context(point_layer, i, UNDO_DELETE);
326 &context, sizeof(context));
328 dynarray_delete_at(point_layer->positions, i);
329 dynarray_delete_at(point_layer->colors, i);
330 dynarray_delete_at(point_layer->ids, i);
334 int point_layer_idle_event(PointLayer *point_layer,
335 const SDL_Event *event,
336 const Camera *camera,
337 UndoHistory *undo_history)
339 trace_assert(point_layer);
341 trace_assert(camera);
344 if (color_picker_event(
345 &point_layer->color_picker,
353 if (point_layer->selected >= 0) {
354 Color *colors = dynarray_data(point_layer->colors);
356 if (!color_picker_drag(&point_layer->color_picker)) {
357 UndoContext context =
358 point_layer_create_undo_context(point_layer, (size_t)point_layer->selected, UNDO_UPDATE);
364 &context, sizeof(context));
366 point_layer->prev_color =
367 color_picker_rgba(&point_layer->color_picker);
370 colors[point_layer->selected] =
371 color_picker_rgba(&point_layer->color_picker);
377 switch (event->type) {
378 case SDL_MOUSEBUTTONDOWN: {
379 switch (event->button.button) {
380 case SDL_BUTTON_LEFT: {
381 const Point position = camera_map_screen(camera, event->button.x, event->button.y);
383 point_layer->selected = point_layer_element_at(
384 point_layer, position);
386 if (point_layer->selected < 0) {
387 point_layer_add_element(
390 color_picker_rgba(&point_layer->color_picker),
393 Color *colors = dynarray_data(point_layer->colors);
394 Point *positions = dynarray_data(point_layer->positions);
396 point_layer->state = POINT_LAYER_MOVE;
397 point_layer->color_picker =
398 create_color_picker_from_rgba(colors[point_layer->selected]);
400 point_layer->prev_color = colors[point_layer->selected];
401 point_layer->prev_position = positions[point_layer->selected];
408 switch (event->key.keysym.sym) {
410 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->positions)) {
411 point_layer_delete_nth_element(
413 (size_t)point_layer->selected,
415 point_layer->selected = -1;
420 if (point_layer->selected >= 0) {
421 char *ids = dynarray_data(point_layer->ids);
422 point_layer->state = POINT_LAYER_EDIT_ID;
424 point_layer->edit_field,
425 ids + ID_MAX_SIZE * point_layer->selected);
426 SDL_StartTextInput();
437 int point_layer_edit_id_event(PointLayer *point_layer,
438 const SDL_Event *event,
439 const Camera *camera,
440 UndoHistory *undo_history)
442 trace_assert(point_layer);
444 trace_assert(camera);
446 switch (event->type) {
448 switch(event->key.keysym.sym) {
450 UndoContext context =
451 point_layer_create_undo_context(point_layer, (size_t) point_layer->selected, UNDO_UPDATE);
460 char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
461 const char *text = edit_field_as_text(point_layer->edit_field);
462 size_t n = min_size_t(strlen(text), ID_MAX_SIZE - 1);
464 memset(id + n, 0, ID_MAX_SIZE - n);
466 point_layer->state = POINT_LAYER_IDLE;
472 point_layer->state = POINT_LAYER_IDLE;
480 return edit_field_event(point_layer->edit_field, event);
484 int point_layer_move_event(PointLayer *point_layer,
485 const SDL_Event *event,
486 const Camera *camera,
487 UndoHistory *undo_history)
489 trace_assert(point_layer);
491 trace_assert(camera);
492 trace_assert(point_layer->selected >= 0);
494 switch (event->type) {
495 case SDL_MOUSEBUTTONUP: {
496 switch (event->button.button) {
497 case SDL_BUTTON_LEFT: {
498 point_layer->state = POINT_LAYER_IDLE;
500 UndoContext context =
501 point_layer_create_undo_context(point_layer, (size_t) point_layer->selected, UNDO_UPDATE);
503 // TODO(#1014): just click (without moving) on the point creates an undo history entry
514 case SDL_MOUSEMOTION: {
515 Point *positions = dynarray_data(point_layer->positions);
516 positions[point_layer->selected] =
517 camera_map_screen(camera, event->motion.x, event->motion.y);
524 int point_layer_event(PointLayer *point_layer,
525 const SDL_Event *event,
526 const Camera *camera,
527 UndoHistory *undo_history)
529 trace_assert(point_layer);
531 trace_assert(camera);
532 trace_assert(undo_history);
534 switch (point_layer->state) {
535 case POINT_LAYER_IDLE:
536 return point_layer_idle_event(point_layer, event, camera, undo_history);
538 case POINT_LAYER_EDIT_ID:
539 return point_layer_edit_id_event(point_layer, event, camera, undo_history);
541 case POINT_LAYER_MOVE:
542 return point_layer_move_event(point_layer, event, camera, undo_history);
548 size_t point_layer_count(const PointLayer *point_layer)
550 trace_assert(point_layer);
551 return dynarray_count(point_layer->positions);
554 const Point *point_layer_positions(const PointLayer *point_layer)
556 trace_assert(point_layer);
557 return dynarray_data(point_layer->positions);
560 const Color *point_layer_colors(const PointLayer *point_layer)
562 trace_assert(point_layer);
563 return dynarray_data(point_layer->colors);
566 const char *point_layer_ids(const PointLayer *point_layer)
568 trace_assert(point_layer);
569 return dynarray_data(point_layer->ids);
572 int point_layer_dump_stream(const PointLayer *point_layer,
575 trace_assert(point_layer);
576 trace_assert(filedump);
578 size_t n = dynarray_count(point_layer->ids);
579 char *ids = dynarray_data(point_layer->ids);
580 Point *positions = dynarray_data(point_layer->positions);
581 Color *colors = dynarray_data(point_layer->colors);
583 fprintf(filedump, "%zd\n", n);
584 for (size_t i = 0; i < n; ++i) {
585 fprintf(filedump, "%s %f %f ",
586 ids + ID_MAX_SIZE * i,
587 positions[i].x, positions[i].y);
588 color_hex_to_stream(colors[i], filedump);
589 fprintf(filedump, "\n");