1 #include "game/camera.h"
3 #include "system/stacktrace.h"
4 #include "system/nth_alloc.h"
5 #include "system/log.h"
8 #include "rect_layer.h"
10 #include "system/line_stream.h"
11 #include "color_picker.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14 #include "undo_history.h"
16 #define RECT_LAYER_ID_MAX_SIZE 36
17 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
18 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
19 #define CREATE_AREA_THRESHOLD 10.0
25 // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
38 ColorPicker color_picker;
43 Edit_field *id_edit_field;
44 // TODO(#1043): RectLayer should use intermediate values instead of previous ones
60 char id[RECT_LAYER_ID_MAX_SIZE];
65 UndoContext create_undo_context(RectLayer *rect_layer, UndoType type)
67 trace_assert(rect_layer);
69 size_t index = type == UNDO_ADD ? dynarray_count(rect_layer->rects) - 1 : (size_t) rect_layer->selection;
71 UndoContext undo_context;
72 undo_context.type = type;
73 undo_context.layer = rect_layer;
74 dynarray_copy_to(rect_layer->rects, &undo_context.rect, index);
75 dynarray_copy_to(rect_layer->colors, &undo_context.color, index);
76 dynarray_copy_to(rect_layer->ids, undo_context.id, index);
77 undo_context.index = index;
83 void rect_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 RectLayer *rect_layer = undo_context->layer;
91 switch (undo_context->type) {
93 dynarray_delete_at(rect_layer->rects, undo_context->index);
94 dynarray_delete_at(rect_layer->colors, undo_context->index);
95 dynarray_delete_at(rect_layer->ids, undo_context->index);
96 rect_layer->selection = -1;
100 dynarray_insert_before(rect_layer->rects, undo_context->index, &undo_context->rect);
101 dynarray_insert_before(rect_layer->colors, undo_context->index, &undo_context->color);
102 dynarray_insert_before(rect_layer->ids, undo_context->index, &undo_context->id);
103 rect_layer->selection = -1;
107 dynarray_replace_at(rect_layer->rects, undo_context->index, &undo_context->rect);
108 dynarray_replace_at(rect_layer->colors, undo_context->index, &undo_context->color);
109 dynarray_replace_at(rect_layer->ids, undo_context->index, &undo_context->id);
114 #define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE) \
116 UndoContext context = create_undo_context(LAYER, UNDO_TYPE); \
125 static int rect_layer_add_rect(RectLayer *layer,
128 UndoHistory *undo_history)
132 if (dynarray_push(layer->rects, &rect) < 0) {
136 if (dynarray_push(layer->colors, &color) < 0) {
140 char id[RECT_LAYER_ID_MAX_SIZE];
141 for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
142 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
144 id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
146 if (dynarray_push(layer->ids, id)) {
150 UNDO_PUSH(layer, undo_history, UNDO_ADD);
155 // TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
156 static int rect_layer_rect_at(RectLayer *layer, Vec position)
160 const size_t n = dynarray_count(layer->rects);
161 Rect *rects = dynarray_data(layer->rects);
163 for (size_t i = 0; i < n; ++i) {
164 if (rect_contains_point(rects[i], position)) {
172 static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
174 const Rect overlay_rect =
176 camera_rect(camera, boundary_rect),
177 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
180 overlay_rect.x + overlay_rect.w,
181 overlay_rect.y + overlay_rect.h,
182 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
183 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
186 static int rect_layer_delete_rect_at(RectLayer *layer,
188 UndoHistory *undo_history)
192 UNDO_PUSH(layer, undo_history, UNDO_DELETE);
194 dynarray_delete_at(layer->rects, i);
195 dynarray_delete_at(layer->colors, i);
196 dynarray_delete_at(layer->ids, i);
201 static int rect_layer_event_idle(RectLayer *layer,
202 const SDL_Event *event,
203 const Camera *camera,
204 UndoHistory *undo_history)
208 trace_assert(camera);
210 int color_changed = 0;
211 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
215 if (color_changed && layer->selection >= 0) {
216 dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
217 layer->state = RECT_LAYER_RECOLOR;
221 switch (event->type) {
222 case SDL_MOUSEBUTTONDOWN: {
223 switch (event->button.button) {
224 case SDL_BUTTON_LEFT: {
225 Point position = camera_map_screen(
229 int rect_at_position =
230 rect_layer_rect_at(layer, position);
232 Rect *rects = dynarray_data(layer->rects);
233 Color *colors = dynarray_data(layer->colors);
235 if (rect_at_position >= 0) {
236 layer->selection = rect_at_position;
237 layer->state = RECT_LAYER_MOVE;
242 rects[layer->selection].x,
243 rects[layer->selection].y));
244 layer->color_picker =
245 create_color_picker_from_rgba(colors[rect_at_position]);
247 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
248 } else if (layer->selection >= 0 && rect_contains_point(
249 rect_layer_resize_anchor(
251 rects[layer->selection]),
253 (float) event->button.x,
254 (float) event->button.y))) {
255 layer->state = RECT_LAYER_RESIZE;
256 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
258 layer->selection = rect_at_position;
260 if (layer->selection < 0) {
261 layer->state = RECT_LAYER_CREATE;
262 layer->create_begin = position;
263 layer->create_end = position;
271 switch (event->key.keysym.sym) {
273 if (layer->selection >= 0) {
274 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
275 layer->selection = -1;
280 if (layer->selection >= 0) {
281 const char *ids = dynarray_data(layer->ids);
282 Color *colors = dynarray_data(layer->colors);
285 layer->id_edit_field,
286 RECT_LAYER_ID_LABEL_SIZE,
287 color_invert(colors[layer->selection]));
289 layer->state = RECT_LAYER_ID_RENAME;
291 layer->id_edit_field,
292 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
293 SDL_StartTextInput();
303 static int rect_layer_event_create(RectLayer *layer,
304 const SDL_Event *event,
305 const Camera *camera,
306 UndoHistory *undo_history)
310 trace_assert(camera);
312 switch (event->type) {
313 case SDL_MOUSEBUTTONUP: {
314 switch (event->button.button) {
315 case SDL_BUTTON_LEFT: {
316 const Rect real_rect =
320 const float area = real_rect.w * real_rect.h;
322 if (area >= CREATE_AREA_THRESHOLD) {
326 color_picker_rgba(&layer->color_picker),
329 log_info("The area is too small %f. Such small box won't be created.\n", area);
331 layer->state = RECT_LAYER_IDLE;
336 case SDL_MOUSEMOTION: {
337 layer->create_end = camera_map_screen(
346 static int rect_layer_event_resize(RectLayer *layer,
347 const SDL_Event *event,
348 const Camera *camera,
349 UndoHistory *undo_history)
353 trace_assert(camera);
354 trace_assert(layer->selection >= 0);
356 switch (event->type) {
357 case SDL_MOUSEMOTION: {
358 layer->inter_rect = rect_from_points(
359 vec(layer->inter_rect.x, layer->inter_rect.y),
365 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
366 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
369 case SDL_MOUSEBUTTONUP: {
370 layer->state = RECT_LAYER_IDLE;
371 UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
372 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
379 static int rect_layer_event_move(RectLayer *layer,
380 const SDL_Event *event,
381 const Camera *camera,
382 UndoHistory *undo_history)
386 trace_assert(camera);
387 trace_assert(layer->selection >= 0);
389 switch (event->type) {
390 case SDL_MOUSEMOTION: {
391 Point position = vec_sub(
398 trace_assert(layer->selection >= 0);
400 layer->inter_rect.x = position.x;
401 layer->inter_rect.y = position.y;
404 case SDL_MOUSEBUTTONUP: {
405 layer->state = RECT_LAYER_IDLE;
406 UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
407 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
413 static int rect_layer_event_id_rename(RectLayer *layer,
414 const SDL_Event *event,
415 const Camera *camera,
416 UndoHistory *undo_history)
420 trace_assert(camera);
421 trace_assert(layer->selection >= 0);
423 switch (event->type) {
425 switch (event->key.keysym.sym) {
427 UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
429 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
430 memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
431 memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
432 layer->state = RECT_LAYER_IDLE;
437 layer->state = RECT_LAYER_IDLE;
444 return edit_field_event(layer->id_edit_field, event);
447 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
456 RectLayer *create_rect_layer(void)
458 Lt *lt = create_lt();
460 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
466 layer->ids = PUSH_LT(
468 create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
470 if (layer->ids == NULL) {
474 layer->rects = PUSH_LT(
476 create_dynarray(sizeof(Rect)),
478 if (layer->rects == NULL) {
482 layer->colors = PUSH_LT(
484 create_dynarray(sizeof(Color)),
486 if (layer->colors == NULL) {
490 layer->id_edit_field = PUSH_LT(
493 RECT_LAYER_ID_LABEL_SIZE,
496 if (layer->id_edit_field == NULL) {
500 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
501 layer->selection = -1;
506 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
508 trace_assert(line_stream);
510 RectLayer *layer = create_rect_layer();
515 const char *line = line_stream_next(line_stream);
517 RETURN_LT(layer->lt, NULL);
521 if (sscanf(line, "%zu", &count) < 0) {
522 RETURN_LT(layer->lt, NULL);
525 for (size_t i = 0; i < count; ++i) {
526 line = line_stream_next(line_stream);
528 RETURN_LT(layer->lt, NULL);
533 char id[RECT_LAYER_ID_MAX_SIZE];
536 "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
541 RETURN_LT(layer->lt, NULL);
544 Color color = hexstr(hex);
546 dynarray_push(layer->rects, &rect);
547 dynarray_push(layer->ids, id);
548 dynarray_push(layer->colors, &color);
554 void destroy_rect_layer(RectLayer *layer)
557 RETURN_LT0(layer->lt);
560 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
563 trace_assert(camera);
565 const size_t n = dynarray_count(layer->rects);
566 Rect *rects = dynarray_data(layer->rects);
567 Color *colors = dynarray_data(layer->colors);
568 const char *ids = dynarray_data(layer->ids);
571 for (size_t i = 0; i < n; ++i) {
572 Rect rect = rects[i];
573 Color color = colors[i];
575 if (layer->selection == (int) i) {
576 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
577 rect = layer->inter_rect;
580 if (layer->state == RECT_LAYER_RECOLOR) {
581 color = layer->inter_color;
585 if (camera_fill_rect(
590 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
595 if ((size_t) layer->selection == i) {
596 const Rect overlay_rect =
598 camera_rect(camera, rect),
599 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
600 const Color overlay_color = color_invert(color);
603 if (camera_fill_rect(
608 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
612 if (camera_draw_thicc_rect_screen(
616 RECT_LAYER_SELECTION_THICCNESS) < 0) {
621 if (layer->state == RECT_LAYER_ID_RENAME) {
622 // ID renaming Edit Field
623 if (edit_field_render_world(
624 layer->id_edit_field,
626 rect_position(rect)) < 0) {
631 if (camera_render_text(
633 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
634 RECT_LAYER_ID_LABEL_SIZE,
636 rect_position(rect)) < 0) {
642 if (camera_fill_rect_screen(
644 rect_layer_resize_anchor(camera, rect),
645 overlay_color) < 0) {
652 const Color color = color_picker_rgba(&layer->color_picker);
653 if (layer->state == RECT_LAYER_CREATE) {
654 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
659 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
667 int rect_layer_event_recolor(RectLayer *layer,
668 const SDL_Event *event,
669 const Camera *camera,
670 UndoHistory *undo_history)
674 trace_assert(camera);
675 trace_assert(undo_history);
676 trace_assert(layer->selection >= 0);
678 int color_changed = 0;
679 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
684 layer->inter_color = color_picker_rgba(&layer->color_picker);
686 if (!color_picker_drag(&layer->color_picker)) {
687 UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
688 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
689 layer->state = RECT_LAYER_IDLE;
696 int rect_layer_event(RectLayer *layer,
697 const SDL_Event *event,
698 const Camera *camera,
699 UndoHistory *undo_history)
703 trace_assert(undo_history);
705 switch (layer->state) {
706 case RECT_LAYER_IDLE:
707 return rect_layer_event_idle(layer, event, camera, undo_history);
709 case RECT_LAYER_CREATE:
710 return rect_layer_event_create(layer, event, camera, undo_history);
712 case RECT_LAYER_RESIZE:
713 return rect_layer_event_resize(layer, event, camera, undo_history);
715 case RECT_LAYER_MOVE:
716 return rect_layer_event_move(layer, event, camera, undo_history);
718 case RECT_LAYER_ID_RENAME:
719 return rect_layer_event_id_rename(layer, event, camera, undo_history);
721 case RECT_LAYER_RECOLOR:
722 return rect_layer_event_recolor(layer, event, camera, undo_history);
728 size_t rect_layer_count(const RectLayer *layer)
730 return dynarray_count(layer->rects);
733 const Rect *rect_layer_rects(const RectLayer *layer)
735 return dynarray_data(layer->rects);
738 const Color *rect_layer_colors(const RectLayer *layer)
740 return dynarray_data(layer->colors);
743 const char *rect_layer_ids(const RectLayer *layer)
745 return dynarray_data(layer->ids);
748 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
751 trace_assert(filedump);
753 size_t n = dynarray_count(layer->ids);
754 char *ids = dynarray_data(layer->ids);
755 Rect *rects = dynarray_data(layer->rects);
756 Color *colors = dynarray_data(layer->colors);
758 fprintf(filedump, "%zd\n", n);
759 for (size_t i = 0; i < n; ++i) {
760 fprintf(filedump, "%s %f %f %f %f ",
761 ids + RECT_LAYER_ID_MAX_SIZE * i,
762 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
763 color_hex_to_stream(colors[i], filedump);
764 fprintf(filedump, "\n");