4 #include "game/camera.h"
6 #include "system/stacktrace.h"
7 #include "system/nth_alloc.h"
8 #include "system/log.h"
11 #include "rect_layer.h"
13 #include "color_picker.h"
14 #include "system/str.h"
15 #include "ui/edit_field.h"
16 #include "undo_history.h"
17 #include "game/level/action.h"
19 #include "math/extrema.h"
21 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
22 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
23 #define CREATE_AREA_THRESHOLD 10.0
24 #define RECT_LAYER_GRID_ROWS 3
25 #define RECT_LAYER_GRID_COLUMNS 4
27 static int rect_clipboard = 0;
28 static Rect rect_clipboard_rect;
29 static Color rect_clipboard_color;
31 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
33 CURSOR_STYLE_RESIZE_VERT, // [1]
34 CURSOR_STYLE_RESIZE_HORIS, // [2]
35 CURSOR_STYLE_RESIZE_DIAG1, // [3]
36 CURSOR_STYLE_RESIZE_VERT, // [4]
38 CURSOR_STYLE_RESIZE_DIAG2, // [6]
40 CURSOR_STYLE_RESIZE_HORIS, // [8]
41 CURSOR_STYLE_RESIZE_DIAG2, // [9]
44 CURSOR_STYLE_RESIZE_DIAG1 // [12]
64 ColorPicker color_picker;
68 Vec2f move_anchor; // The mouse offset from the left-top
69 // corner of the rect during moving it
70 Edit_field id_edit_field;
74 const char *id_name_prefix;
95 char id[ENTITY_MAX_ID_SIZE];
116 UndoElementContext element;
117 UndoSwapContext swap;
121 RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
124 trace_assert(index < layer->rects.count);
126 RectUndoContext undo_context;
127 undo_context.add.type = RECT_UNDO_ADD;
128 undo_context.add.layer = layer;
129 undo_context.add.index = index;
134 RectUndoContext create_rect_undo_element_context(RectLayer *layer)
137 size_t index = (size_t) layer->selection;
138 trace_assert(index < layer->rects.count);
140 RectUndoContext undo_context;
141 undo_context.element.layer = layer;
142 undo_context.element.index = index;
143 dynarray_copy_to(&layer->rects, &undo_context.element.rect, index);
144 dynarray_copy_to(&layer->colors, &undo_context.element.color, index);
145 dynarray_copy_to(&layer->ids, undo_context.element.id, index);
146 dynarray_copy_to(&layer->actions, &undo_context.element.action, index);
151 RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer)
153 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
154 undo_context.type = RECT_UNDO_UPDATE;
159 RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer)
161 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
162 undo_context.type = RECT_UNDO_DELETE;
167 RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
169 RectUndoContext undo_context;
170 undo_context.swap.type = RECT_UNDO_SWAP;
171 undo_context.swap.layer = rect_layer;
172 undo_context.swap.index1 = index1;
173 undo_context.swap.index2 = index2;
178 void rect_layer_undo(void *context, size_t context_size)
180 trace_assert(context);
181 trace_assert(sizeof(RectUndoContext) == context_size);
183 RectUndoContext *undo_context = context;
185 switch (undo_context->type) {
186 case RECT_UNDO_ADD: {
187 RectLayer *layer = undo_context->add.layer;
188 dynarray_delete_at(&layer->rects, undo_context->add.index);
189 dynarray_delete_at(&layer->colors, undo_context->add.index);
190 dynarray_delete_at(&layer->ids, undo_context->add.index);
191 dynarray_delete_at(&layer->actions, undo_context->add.index);
192 layer->selection = -1;
195 case RECT_UNDO_DELETE: {
196 RectLayer *layer = undo_context->element.layer;
197 dynarray_insert_before(&layer->rects, undo_context->element.index, &undo_context->element.rect);
198 dynarray_insert_before(&layer->colors, undo_context->element.index, &undo_context->element.color);
199 dynarray_insert_before(&layer->ids, undo_context->element.index, &undo_context->element.id);
200 dynarray_insert_before(&layer->actions, undo_context->element.index, &undo_context->element.action);
201 layer->selection = -1;
204 case RECT_UNDO_UPDATE: {
205 RectLayer *layer = undo_context->element.layer;
206 dynarray_replace_at(&layer->rects, undo_context->element.index, &undo_context->element.rect);
207 dynarray_replace_at(&layer->colors, undo_context->element.index, &undo_context->element.color);
208 dynarray_replace_at(&layer->ids, undo_context->element.index, &undo_context->element.id);
209 dynarray_replace_at(&layer->actions, undo_context->element.index, &undo_context->element.action);
212 case RECT_UNDO_SWAP: {
213 RectLayer *layer = undo_context->element.layer;
214 dynarray_swap(&layer->rects, undo_context->swap.index1, undo_context->swap.index2);
215 dynarray_swap(&layer->colors, undo_context->swap.index1, undo_context->swap.index2);
216 dynarray_swap(&layer->ids, undo_context->swap.index1, undo_context->swap.index2);
217 dynarray_swap(&layer->actions, undo_context->swap.index1, undo_context->swap.index2);
222 #define RECT_UNDO_PUSH(HISTORY, CONTEXT) \
224 RectUndoContext context = (CONTEXT); \
232 static int rect_layer_add_rect(RectLayer *layer,
235 UndoHistory *undo_history)
239 dynarray_push(&layer->rects, &rect);
240 dynarray_push(&layer->colors, &color);
242 char id[ENTITY_MAX_ID_SIZE];
243 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
244 layer->id_name_prefix,
245 layer->id_name_counter++);
246 dynarray_push(&layer->ids, id);
248 dynarray_push_empty(&layer->actions);
252 create_rect_undo_add_context(
254 layer->rects.count - 1));
259 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
263 int n = (int) layer->rects.count;
264 Rect *rects = (Rect*)layer->rects.data;
266 for (int i = n - 1; i >= 0; --i) {
267 if (rect_contains_point(rects[i], position)) {
275 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
276 UndoHistory *undo_history)
279 trace_assert(a < layer->rects.count);
280 trace_assert(b < layer->rects.count);
282 dynarray_swap(&layer->rects, a, b);
283 dynarray_swap(&layer->colors, a, b);
284 dynarray_swap(&layer->ids, a, b);
285 dynarray_swap(&layer->actions, a, b);
287 RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
290 static int rect_layer_delete_rect_at(RectLayer *layer,
292 UndoHistory *undo_history)
296 RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer));
298 dynarray_delete_at(&layer->rects, i);
299 dynarray_delete_at(&layer->colors, i);
300 dynarray_delete_at(&layer->ids, i);
301 dynarray_delete_at(&layer->actions, i);
306 static int calc_resize_mask(Vec2f point, Rect rect)
309 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
310 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
311 mask = mask | (1 << side);
317 static int rect_layer_event_idle(RectLayer *layer,
318 const SDL_Event *event,
319 const Camera *camera,
320 UndoHistory *undo_history)
324 trace_assert(camera);
326 int color_changed = 0;
327 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
332 if (layer->selection >= 0) {
333 dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
334 layer->state = RECT_LAYER_RECOLOR;
339 Rect *rects = (Rect*)layer->rects.data;
341 switch (event->type) {
342 case SDL_MOUSEBUTTONDOWN: {
343 switch (event->button.button) {
344 case SDL_BUTTON_LEFT: {
345 Vec2f position = camera_map_screen(
349 int rect_at_position =
350 rect_layer_rect_at(layer, position);
353 Color *colors = (Color*)layer->colors.data;
355 if (layer->selection >= 0 &&
356 layer->selection == rect_at_position &&
357 (layer->resize_mask = calc_resize_mask(
358 vec((float) event->button.x, (float)event->button.y),
359 camera_rect(camera, rects[layer->selection])))) {
360 layer->state = RECT_LAYER_RESIZE;
361 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
362 } else if (rect_at_position >= 0) {
363 layer->selection = rect_at_position;
364 layer->state = RECT_LAYER_MOVE;
365 layer->move_anchor = vec_sub(
368 rects[layer->selection].x,
369 rects[layer->selection].y));
370 layer->color_picker =
371 create_color_picker_from_rgba(colors[rect_at_position]);
372 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
374 layer->selection = rect_at_position;
376 if (layer->selection < 0) {
377 layer->state = RECT_LAYER_CREATE;
378 layer->create_begin = position;
379 layer->create_end = position;
386 case SDL_MOUSEMOTION: {
388 Vec2f position = camera_map_screen(
392 if (layer->selection >= 0 &&
393 layer->selection == rect_layer_rect_at(layer, position) &&
394 (resize_mask = calc_resize_mask(
395 vec((float) event->button.x, (float)event->button.y),
396 camera_rect(camera, rects[layer->selection])))) {
397 layer->cursor->style = resize_styles[resize_mask];
399 layer->cursor->style = CURSOR_STYLE_POINTER;
404 switch (event->key.keysym.sym) {
406 if ((event->key.keysym.mod & KMOD_SHIFT)
407 && (layer->selection >= 0)
408 && ((size_t)(layer->selection + 1) < layer->rects.count)) {
409 rect_layer_swap_elements(
411 (size_t) layer->selection,
412 (size_t) layer->selection + 1,
419 if ((event->key.keysym.mod & KMOD_SHIFT)
420 && (layer->selection > 0)
421 && ((size_t) layer->selection < layer->rects.count)) {
422 rect_layer_swap_elements(
424 (size_t) layer->selection,
425 (size_t) layer->selection - 1,
432 if (layer->selection >= 0) {
433 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
434 layer->selection = -1;
439 // TODO(#1171): there is no UI indication that we are in the snapping mode
440 layer->snapping_enabled = !layer->snapping_enabled;
444 if (layer->selection >= 0) {
445 const char *ids = (char*)layer->ids.data;
446 Color *colors = (Color*)layer->colors.data;
449 &layer->id_edit_field,
450 RECT_LAYER_ID_LABEL_SIZE,
451 color_invert(colors[layer->selection]));
453 layer->state = RECT_LAYER_ID_RENAME;
455 &layer->id_edit_field,
456 ids + layer->selection * ENTITY_MAX_ID_SIZE);
457 SDL_StartTextInput();
462 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
464 dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
465 dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
470 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
472 SDL_GetMouseState(&x, &y);
473 Vec2f position = camera_map_screen(camera, x, y);
477 rect(position.x, position.y,
478 rect_clipboard_rect.w, rect_clipboard_rect.h),
479 rect_clipboard_color,
490 static int rect_layer_event_create(RectLayer *layer,
491 const SDL_Event *event,
492 const Camera *camera,
493 UndoHistory *undo_history)
497 trace_assert(camera);
499 switch (event->type) {
500 case SDL_MOUSEBUTTONUP: {
501 switch (event->button.button) {
502 case SDL_BUTTON_LEFT: {
503 const Rect real_rect =
507 const float area = real_rect.w * real_rect.h;
509 if (area >= CREATE_AREA_THRESHOLD) {
513 color_picker_rgba(&layer->color_picker),
516 log_info("The area is too small %f. Such small box won't be created.\n", area);
518 layer->state = RECT_LAYER_IDLE;
523 case SDL_MOUSEMOTION: {
524 layer->create_end = camera_map_screen(
534 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
537 trace_assert(layer->selection >= 0);
540 if (!layer->snapping_enabled) return;
542 Rect *rects = (Rect*)layer->rects.data;
543 size_t rects_size = layer->rects.count;
545 for (size_t i = 0; i < rects_size; ++i) {
546 if (i == (size_t) layer->selection) continue;
548 const Rect b = rects[i];
550 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
551 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
554 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
555 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
560 static int rect_layer_event_resize(RectLayer *layer,
561 const SDL_Event *event,
562 const Camera *camera,
563 UndoHistory *undo_history)
567 trace_assert(camera);
568 trace_assert(layer->selection >= 0);
570 Rect *rects = (Rect*)layer->rects.data;
572 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
574 switch (event->type) {
575 case SDL_MOUSEMOTION: {
576 Vec2f position = camera_map_screen(
581 switch (layer->resize_mask) {
583 Rect a = rect(rects[layer->selection].x,
585 rects[layer->selection].w,
586 rects[layer->selection].h);
588 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
590 layer->inter_rect = rect_from_points(
592 rect_position2(rects[layer->selection]));
596 Rect a = rect(position.x,
597 rects[layer->selection].y,
598 rects[layer->selection].w,
599 rects[layer->selection].h);
601 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
603 layer->inter_rect = rect_from_points(
605 rect_position2(rects[layer->selection]));
608 case 3: { // TOP,LEFT
612 rects[layer->selection].w,
613 rects[layer->selection].h);
615 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
617 layer->inter_rect = rect_from_points(
619 rect_position2(rects[layer->selection]));
623 Rect a = rect(rects[layer->selection].x,
625 rects[layer->selection].w,
626 rects[layer->selection].h);
628 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
630 layer->inter_rect = rect_from_points(
631 rect_position(rects[layer->selection]),
632 vec(rects[layer->selection].x + rects[layer->selection].w,
636 case 6: { // BOTTOM,LEFT
640 rects[layer->selection].w,
641 -rects[layer->selection].h);
643 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
645 layer->inter_rect = rect_from_points(
646 vec(a.x, rects[layer->selection].y),
647 vec(rects[layer->selection].x + rects[layer->selection].w,
652 Rect a = rect(position.x,
653 rects[layer->selection].y,
654 rects[layer->selection].w,
655 rects[layer->selection].h);
657 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
659 layer->inter_rect = rect_from_points(
660 rect_position(rects[layer->selection]),
661 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
664 case 9: { // TOP,RIGHT
668 -rects[layer->selection].w,
669 rects[layer->selection].h);
671 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
673 layer->inter_rect = rect_from_points(
674 vec(rects[layer->selection].x, a.y),
676 rects[layer->selection].y + rects[layer->selection].h));
679 case 12: { // BOTTOM,RIGHT
683 -rects[layer->selection].w,
684 -rects[layer->selection].h);
686 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
688 layer->inter_rect = rect_from_points(
689 rect_position(rects[layer->selection]),
696 case SDL_MOUSEBUTTONUP: {
697 layer->state = RECT_LAYER_IDLE;
698 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
699 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
707 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
708 float snapping_threshold)
712 trace_assert(layer->selection >= 0);
714 if (!layer->snapping_enabled) return;
716 Rect *rects = (Rect*)layer->rects.data;
717 size_t rects_size = layer->rects.count;
719 for (size_t i = 0; i < rects_size; ++i) {
720 if (i == (size_t) layer->selection) continue;
722 const Rect b = rects[i];
724 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
725 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
728 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
729 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
734 static int rect_layer_event_move(RectLayer *layer,
735 const SDL_Event *event,
736 const Camera *camera,
737 UndoHistory *undo_history)
741 trace_assert(camera);
742 trace_assert(layer->selection >= 0);
744 Rect *rects = (Rect*)layer->rects.data;
746 switch (event->type) {
747 case SDL_MOUSEMOTION: {
748 const Uint8 *state = SDL_GetKeyboardState(NULL);
749 const Vec2f mouse_pos = vec_sub(
756 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
757 layer->inter_rect.x = mouse_pos.x;
758 layer->inter_rect.y = mouse_pos.y;
760 const Vec2f rect_pos = rect_position(rects[layer->selection]);
762 const float dx = fabsf(rect_pos.x - mouse_pos.x);
763 const float dy = fabsf(rect_pos.y - mouse_pos.y);
766 layer->inter_rect.x = mouse_pos.x;
767 layer->inter_rect.y = rect_pos.y;
769 layer->inter_rect.x = rect_pos.x;
770 layer->inter_rect.y = mouse_pos.y;
774 snap_rect_move_if_enabled(layer, &layer->inter_rect,
775 SNAPPING_THRESHOLD / camera->scale);
778 case SDL_MOUSEBUTTONUP: {
779 layer->state = RECT_LAYER_IDLE;
781 float distance = vec_length(
782 vec_sub(rect_position(layer->inter_rect),
783 rect_position(rects[layer->selection])));
785 if (distance > 1e-6) {
786 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
787 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
794 static int rect_layer_event_id_rename(RectLayer *layer,
795 const SDL_Event *event,
796 const Camera *camera,
797 UndoHistory *undo_history)
801 trace_assert(camera);
802 trace_assert(layer->selection >= 0);
804 switch (event->type) {
806 switch (event->key.keysym.sym) {
808 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
810 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
811 memset(id, 0, ENTITY_MAX_ID_SIZE);
812 memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
813 layer->state = RECT_LAYER_IDLE;
818 layer->state = RECT_LAYER_IDLE;
825 return edit_field_event(&layer->id_edit_field, event);
828 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
837 RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
839 trace_assert(cursor);
841 Lt *lt = create_lt();
843 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
849 layer->ids = create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE);
850 layer->rects = create_dynarray(sizeof(Rect));
851 layer->colors = create_dynarray(sizeof(Color));
852 layer->actions = create_dynarray(sizeof(Action));
854 layer->id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
855 layer->id_edit_field.font_color = COLOR_BLACK;
857 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
858 layer->selection = -1;
859 layer->id_name_prefix = id_name_prefix;
860 layer->cursor = cursor;
865 RectLayer *chop_rect_layer(Memory *memory,
867 const char *id_name_prefix,
870 trace_assert(memory);
873 RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
876 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
877 char id[ENTITY_MAX_ID_SIZE];
878 for (int i = 0; i < n; ++i) {
880 String line = trim(chop_by_delim(input, '\n'));
881 String string_id = trim(chop_word(&line));
882 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
883 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
884 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
885 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
886 Color color = hexs(trim(chop_word(&line)));
888 memset(id, 0, ENTITY_MAX_ID_SIZE);
892 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
894 dynarray_push(&layer->rects, &rect);
895 dynarray_push(&layer->colors, &color);
896 dynarray_push(&layer->ids, id);
903 String action_string = trim(chop_word(&line));
904 if (action_string.count > 0) {
905 action.type = (ActionType)atol(string_to_cstr(memory, action_string));
906 switch (action.type) {
907 case ACTION_NONE: break;
908 case ACTION_TOGGLE_GOAL:
909 case ACTION_HIDE_LABEL: {
910 String label_id = trim(chop_word(&line));
911 trace_assert(label_id.count > 0);
912 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
913 memcpy(action.entity_id,
916 ENTITY_MAX_ID_SIZE - 1,
920 case ACTION_N: break;
924 dynarray_push(&layer->actions, &action);
930 void destroy_rect_layer(RectLayer *layer)
934 free(layer->ids.data);
935 free(layer->rects.data);
936 free(layer->colors.data);
937 free(layer->actions.data);
939 RETURN_LT0(layer->lt);
942 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
945 trace_assert(camera);
947 const size_t n = layer->rects.count;
948 Rect *rects = (Rect *)layer->rects.data;
949 Color *colors = (Color *)layer->colors.data;
950 const char *ids = (const char *)layer->ids.data;
953 for (size_t i = 0; i < n; ++i) {
954 Rect rect = rects[i];
955 Color color = colors[i];
957 if (layer->selection == (int) i) {
958 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
959 rect = layer->inter_rect;
962 if (layer->state == RECT_LAYER_RECOLOR) {
963 color = layer->inter_color;
968 if (camera_fill_rect(
973 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
979 if (active && layer->selection >= 0) {
980 Rect rect = rects[layer->selection];
981 Color color = colors[layer->selection];
983 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
984 rect = layer->inter_rect;
987 if (layer->state == RECT_LAYER_RECOLOR) {
988 color = layer->inter_color;
991 const Rect overlay_rect =
993 camera_rect(camera, rect),
994 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
995 const Color overlay_color = color_invert(color);
998 if (camera_draw_thicc_rect_screen(
1002 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1006 const Vec2f rect_id_pos = vec_sub(
1007 rect_position(rect),
1009 RECT_LAYER_ID_LABEL_SIZE,
1010 vec(0.0f, FONT_CHAR_HEIGHT)));
1013 if (layer->state == RECT_LAYER_ID_RENAME) {
1014 // ID renaming Edit Field
1015 if (edit_field_render_world(
1016 &layer->id_edit_field,
1023 if (camera_render_text(
1025 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1026 RECT_LAYER_ID_LABEL_SIZE,
1027 color_invert(color),
1035 const Color color = color_picker_rgba(&layer->color_picker);
1036 if (layer->state == RECT_LAYER_CREATE) {
1037 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1042 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1050 int rect_layer_event_recolor(RectLayer *layer,
1051 const SDL_Event *event,
1052 const Camera *camera,
1053 UndoHistory *undo_history)
1055 trace_assert(layer);
1056 trace_assert(event);
1057 trace_assert(camera);
1058 trace_assert(undo_history);
1059 trace_assert(layer->selection >= 0);
1061 int color_changed = 0;
1062 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1066 if (color_changed) {
1067 layer->inter_color = color_picker_rgba(&layer->color_picker);
1069 if (!color_picker_drag(&layer->color_picker)) {
1070 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
1071 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1072 layer->state = RECT_LAYER_IDLE;
1079 int rect_layer_event(RectLayer *layer,
1080 const SDL_Event *event,
1081 const Camera *camera,
1082 UndoHistory *undo_history)
1084 trace_assert(layer);
1085 trace_assert(event);
1086 trace_assert(undo_history);
1088 switch (layer->state) {
1089 case RECT_LAYER_IDLE:
1090 return rect_layer_event_idle(layer, event, camera, undo_history);
1092 case RECT_LAYER_CREATE:
1093 return rect_layer_event_create(layer, event, camera, undo_history);
1095 case RECT_LAYER_RESIZE:
1096 return rect_layer_event_resize(layer, event, camera, undo_history);
1098 case RECT_LAYER_MOVE:
1099 return rect_layer_event_move(layer, event, camera, undo_history);
1101 case RECT_LAYER_ID_RENAME:
1102 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1104 case RECT_LAYER_RECOLOR:
1105 return rect_layer_event_recolor(layer, event, camera, undo_history);
1112 size_t rect_layer_count(const RectLayer *layer)
1114 return layer->rects.count;
1117 const Rect *rect_layer_rects(const RectLayer *layer)
1119 return (const Rect *)layer->rects.data;
1122 const Color *rect_layer_colors(const RectLayer *layer)
1124 return (const Color *)layer->colors.data;
1127 const char *rect_layer_ids(const RectLayer *layer)
1129 return (const char *)layer->ids.data;
1132 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1134 trace_assert(layer);
1135 trace_assert(filedump);
1137 size_t n = layer->ids.count;
1138 char *ids = (char *)layer->ids.data;
1139 Rect *rects = (Rect *)layer->rects.data;
1140 Color *colors = (Color *)layer->colors.data;
1141 Action *actions = (Action *)layer->actions.data;
1143 fprintf(filedump, "%zd\n", n);
1144 for (size_t i = 0; i < n; ++i) {
1145 fprintf(filedump, "%s %f %f %f %f ",
1146 ids + ENTITY_MAX_ID_SIZE * i,
1147 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1148 color_hex_to_stream(colors[i], filedump);
1150 switch (actions[i].type) {
1151 case ACTION_NONE: {} break;
1153 case ACTION_TOGGLE_GOAL:
1154 case ACTION_HIDE_LABEL: {
1155 fprintf(filedump, " %d %.*s",
1156 (int)actions[i].type,
1157 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1159 case ACTION_N: break;
1162 fprintf(filedump, "\n");
1168 const Action *rect_layer_actions(const RectLayer *layer)
1170 return (const Action *)layer->actions.data;