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_SELECTION_THICCNESS 10.0f
17 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
18 #define CREATE_AREA_THRESHOLD 10.0
20 // TODO(#1075): there is no way to modify z order in Rect, Point and Label Layers
22 // TODO(#1051): RectLayer does not support copy-pasting
24 static int clipboard = 0;
25 static Rect clipboard_rect;
26 static Color clipboard_color;
31 // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
44 ColorPicker color_picker;
49 Edit_field *id_edit_field;
53 const char *id_name_prefix;
67 char id[RECT_LAYER_ID_MAX_SIZE];
72 UndoContext create_undo_context(RectLayer *rect_layer, UndoType type)
74 trace_assert(rect_layer);
76 size_t index = type == UNDO_ADD ? dynarray_count(rect_layer->rects) - 1 : (size_t) rect_layer->selection;
78 UndoContext undo_context;
79 undo_context.type = type;
80 undo_context.layer = rect_layer;
81 dynarray_copy_to(rect_layer->rects, &undo_context.rect, index);
82 dynarray_copy_to(rect_layer->colors, &undo_context.color, index);
83 dynarray_copy_to(rect_layer->ids, undo_context.id, index);
84 undo_context.index = index;
90 void rect_layer_undo(void *context, size_t context_size)
92 trace_assert(context);
93 trace_assert(sizeof(UndoContext) == context_size);
95 UndoContext *undo_context = context;
96 RectLayer *rect_layer = undo_context->layer;
98 switch (undo_context->type) {
100 dynarray_delete_at(rect_layer->rects, undo_context->index);
101 dynarray_delete_at(rect_layer->colors, undo_context->index);
102 dynarray_delete_at(rect_layer->ids, undo_context->index);
103 rect_layer->selection = -1;
107 dynarray_insert_before(rect_layer->rects, undo_context->index, &undo_context->rect);
108 dynarray_insert_before(rect_layer->colors, undo_context->index, &undo_context->color);
109 dynarray_insert_before(rect_layer->ids, undo_context->index, &undo_context->id);
110 rect_layer->selection = -1;
114 dynarray_replace_at(rect_layer->rects, undo_context->index, &undo_context->rect);
115 dynarray_replace_at(rect_layer->colors, undo_context->index, &undo_context->color);
116 dynarray_replace_at(rect_layer->ids, undo_context->index, &undo_context->id);
121 #define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE) \
123 UndoContext context = create_undo_context(LAYER, UNDO_TYPE); \
132 static int rect_layer_add_rect(RectLayer *layer,
135 UndoHistory *undo_history)
139 if (dynarray_push(layer->rects, &rect) < 0) {
143 if (dynarray_push(layer->colors, &color) < 0) {
147 char id[RECT_LAYER_ID_MAX_SIZE];
148 snprintf(id, RECT_LAYER_ID_MAX_SIZE, "%s_%d",
149 layer->id_name_prefix,
150 layer->id_name_counter++);
151 if (dynarray_push(layer->ids, id)) {
155 UNDO_PUSH(layer, undo_history, UNDO_ADD);
160 // TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
161 static int rect_layer_rect_at(RectLayer *layer, Vec position)
165 int n = (int) dynarray_count(layer->rects);
166 Rect *rects = dynarray_data(layer->rects);
168 for (int i = n - 1; i >= 0; --i) {
169 if (rect_contains_point(rects[i], position)) {
177 static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
179 const Rect overlay_rect =
181 camera_rect(camera, boundary_rect),
182 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
185 overlay_rect.x + overlay_rect.w,
186 overlay_rect.y + overlay_rect.h,
187 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
188 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
191 static int rect_layer_delete_rect_at(RectLayer *layer,
193 UndoHistory *undo_history)
197 UNDO_PUSH(layer, undo_history, UNDO_DELETE);
199 dynarray_delete_at(layer->rects, i);
200 dynarray_delete_at(layer->colors, i);
201 dynarray_delete_at(layer->ids, i);
206 static int rect_layer_event_idle(RectLayer *layer,
207 const SDL_Event *event,
208 const Camera *camera,
209 UndoHistory *undo_history)
213 trace_assert(camera);
215 int color_changed = 0;
216 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
221 if (layer->selection >= 0) {
222 dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
223 layer->state = RECT_LAYER_RECOLOR;
228 switch (event->type) {
229 case SDL_MOUSEBUTTONDOWN: {
230 switch (event->button.button) {
231 case SDL_BUTTON_LEFT: {
232 Point position = camera_map_screen(
236 int rect_at_position =
237 rect_layer_rect_at(layer, position);
239 Rect *rects = dynarray_data(layer->rects);
240 Color *colors = dynarray_data(layer->colors);
242 if (rect_at_position >= 0) {
243 layer->selection = rect_at_position;
244 layer->state = RECT_LAYER_MOVE;
249 rects[layer->selection].x,
250 rects[layer->selection].y));
251 layer->color_picker =
252 create_color_picker_from_rgba(colors[rect_at_position]);
254 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
255 } else if (layer->selection >= 0 && rect_contains_point(
256 rect_layer_resize_anchor(
258 rects[layer->selection]),
260 (float) event->button.x,
261 (float) event->button.y))) {
262 layer->state = RECT_LAYER_RESIZE;
263 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
265 layer->selection = rect_at_position;
267 if (layer->selection < 0) {
268 layer->state = RECT_LAYER_CREATE;
269 layer->create_begin = position;
270 layer->create_end = position;
278 switch (event->key.keysym.sym) {
280 if (layer->selection >= 0) {
281 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
282 layer->selection = -1;
287 if (layer->selection >= 0) {
288 const char *ids = dynarray_data(layer->ids);
289 Color *colors = dynarray_data(layer->colors);
292 layer->id_edit_field,
293 RECT_LAYER_ID_LABEL_SIZE,
294 color_invert(colors[layer->selection]));
296 layer->state = RECT_LAYER_ID_RENAME;
298 layer->id_edit_field,
299 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
300 SDL_StartTextInput();
305 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
307 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
308 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
313 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
315 SDL_GetMouseState(&x, &y);
316 Point position = camera_map_screen(camera, x, y);
320 rect(position.x, position.y,
321 clipboard_rect.w, clipboard_rect.h),
333 static int rect_layer_event_create(RectLayer *layer,
334 const SDL_Event *event,
335 const Camera *camera,
336 UndoHistory *undo_history)
340 trace_assert(camera);
342 switch (event->type) {
343 case SDL_MOUSEBUTTONUP: {
344 switch (event->button.button) {
345 case SDL_BUTTON_LEFT: {
346 const Rect real_rect =
350 const float area = real_rect.w * real_rect.h;
352 if (area >= CREATE_AREA_THRESHOLD) {
356 color_picker_rgba(&layer->color_picker),
359 log_info("The area is too small %f. Such small box won't be created.\n", area);
361 layer->state = RECT_LAYER_IDLE;
366 case SDL_MOUSEMOTION: {
367 layer->create_end = camera_map_screen(
376 static int rect_layer_event_resize(RectLayer *layer,
377 const SDL_Event *event,
378 const Camera *camera,
379 UndoHistory *undo_history)
383 trace_assert(camera);
384 trace_assert(layer->selection >= 0);
386 switch (event->type) {
387 case SDL_MOUSEMOTION: {
388 layer->inter_rect = rect_from_points(
389 vec(layer->inter_rect.x, layer->inter_rect.y),
395 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
396 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
399 case SDL_MOUSEBUTTONUP: {
400 layer->state = RECT_LAYER_IDLE;
401 UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
402 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
409 static int rect_layer_event_move(RectLayer *layer,
410 const SDL_Event *event,
411 const Camera *camera,
412 UndoHistory *undo_history)
416 trace_assert(camera);
417 trace_assert(layer->selection >= 0);
419 switch (event->type) {
420 case SDL_MOUSEMOTION: {
421 Point position = vec_sub(
428 trace_assert(layer->selection >= 0);
430 layer->inter_rect.x = position.x;
431 layer->inter_rect.y = position.y;
434 case SDL_MOUSEBUTTONUP: {
435 layer->state = RECT_LAYER_IDLE;
436 UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
437 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
443 static int rect_layer_event_id_rename(RectLayer *layer,
444 const SDL_Event *event,
445 const Camera *camera,
446 UndoHistory *undo_history)
450 trace_assert(camera);
451 trace_assert(layer->selection >= 0);
453 switch (event->type) {
455 switch (event->key.keysym.sym) {
457 UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
459 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
460 memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
461 memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
462 layer->state = RECT_LAYER_IDLE;
467 layer->state = RECT_LAYER_IDLE;
474 return edit_field_event(layer->id_edit_field, event);
477 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
486 RectLayer *create_rect_layer(const char *id_name_prefix)
488 Lt *lt = create_lt();
490 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
496 layer->ids = PUSH_LT(
498 create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
500 if (layer->ids == NULL) {
504 layer->rects = PUSH_LT(
506 create_dynarray(sizeof(Rect)),
508 if (layer->rects == NULL) {
512 layer->colors = PUSH_LT(
514 create_dynarray(sizeof(Color)),
516 if (layer->colors == NULL) {
520 layer->id_edit_field = PUSH_LT(
523 RECT_LAYER_ID_LABEL_SIZE,
526 if (layer->id_edit_field == NULL) {
530 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
531 layer->selection = -1;
532 layer->id_name_prefix = id_name_prefix;
537 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
539 trace_assert(line_stream);
541 RectLayer *layer = create_rect_layer(id_name_prefix);
546 const char *line = line_stream_next(line_stream);
548 RETURN_LT(layer->lt, NULL);
552 if (sscanf(line, "%zu", &count) < 0) {
553 RETURN_LT(layer->lt, NULL);
556 for (size_t i = 0; i < count; ++i) {
557 line = line_stream_next(line_stream);
559 RETURN_LT(layer->lt, NULL);
564 char id[RECT_LAYER_ID_MAX_SIZE];
567 "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
572 RETURN_LT(layer->lt, NULL);
575 Color color = hexstr(hex);
577 dynarray_push(layer->rects, &rect);
578 dynarray_push(layer->ids, id);
579 dynarray_push(layer->colors, &color);
585 void destroy_rect_layer(RectLayer *layer)
588 RETURN_LT0(layer->lt);
591 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
594 trace_assert(camera);
596 const size_t n = dynarray_count(layer->rects);
597 Rect *rects = dynarray_data(layer->rects);
598 Color *colors = dynarray_data(layer->colors);
599 const char *ids = dynarray_data(layer->ids);
602 for (size_t i = 0; i < n; ++i) {
603 Rect rect = rects[i];
604 Color color = colors[i];
606 if (layer->selection == (int) i) {
607 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
608 rect = layer->inter_rect;
611 if (layer->state == RECT_LAYER_RECOLOR) {
612 color = layer->inter_color;
616 if (camera_fill_rect(
621 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
626 if (active && (size_t) layer->selection == i) {
627 const Rect overlay_rect =
629 camera_rect(camera, rect),
630 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
631 const Color overlay_color = color_invert(color);
634 if (camera_fill_rect(
639 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
643 if (camera_draw_thicc_rect_screen(
647 RECT_LAYER_SELECTION_THICCNESS) < 0) {
652 if (layer->state == RECT_LAYER_ID_RENAME) {
653 // ID renaming Edit Field
654 if (edit_field_render_world(
655 layer->id_edit_field,
657 rect_position(rect)) < 0) {
662 if (camera_render_text(
664 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
665 RECT_LAYER_ID_LABEL_SIZE,
667 rect_position(rect)) < 0) {
673 if (camera_fill_rect_screen(
675 rect_layer_resize_anchor(camera, rect),
676 overlay_color) < 0) {
683 const Color color = color_picker_rgba(&layer->color_picker);
684 if (layer->state == RECT_LAYER_CREATE) {
685 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
690 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
698 int rect_layer_event_recolor(RectLayer *layer,
699 const SDL_Event *event,
700 const Camera *camera,
701 UndoHistory *undo_history)
705 trace_assert(camera);
706 trace_assert(undo_history);
707 trace_assert(layer->selection >= 0);
709 int color_changed = 0;
710 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
715 layer->inter_color = color_picker_rgba(&layer->color_picker);
717 if (!color_picker_drag(&layer->color_picker)) {
718 UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
719 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
720 layer->state = RECT_LAYER_IDLE;
727 int rect_layer_event(RectLayer *layer,
728 const SDL_Event *event,
729 const Camera *camera,
730 UndoHistory *undo_history)
734 trace_assert(undo_history);
736 switch (layer->state) {
737 case RECT_LAYER_IDLE:
738 return rect_layer_event_idle(layer, event, camera, undo_history);
740 case RECT_LAYER_CREATE:
741 return rect_layer_event_create(layer, event, camera, undo_history);
743 case RECT_LAYER_RESIZE:
744 return rect_layer_event_resize(layer, event, camera, undo_history);
746 case RECT_LAYER_MOVE:
747 return rect_layer_event_move(layer, event, camera, undo_history);
749 case RECT_LAYER_ID_RENAME:
750 return rect_layer_event_id_rename(layer, event, camera, undo_history);
752 case RECT_LAYER_RECOLOR:
753 return rect_layer_event_recolor(layer, event, camera, undo_history);
759 size_t rect_layer_count(const RectLayer *layer)
761 return dynarray_count(layer->rects);
764 const Rect *rect_layer_rects(const RectLayer *layer)
766 return dynarray_data(layer->rects);
769 const Color *rect_layer_colors(const RectLayer *layer)
771 return dynarray_data(layer->colors);
774 const char *rect_layer_ids(const RectLayer *layer)
776 return dynarray_data(layer->ids);
779 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
782 trace_assert(filedump);
784 size_t n = dynarray_count(layer->ids);
785 char *ids = dynarray_data(layer->ids);
786 Rect *rects = dynarray_data(layer->rects);
787 Color *colors = dynarray_data(layer->colors);
789 fprintf(filedump, "%zd\n", n);
790 for (size_t i = 0; i < n; ++i) {
791 fprintf(filedump, "%s %f %f %f %f ",
792 ids + RECT_LAYER_ID_MAX_SIZE * i,
793 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
794 color_hex_to_stream(colors[i], filedump);
795 fprintf(filedump, "\n");