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 "system/str.h"
14 #include "undo_history.h"
15 #include "game/level/action.h"
17 #include "math/extrema.h"
19 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
20 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
21 #define CREATE_AREA_THRESHOLD 10.0
23 static int rect_clipboard = 0;
24 static Rect rect_clipboard_rect;
25 static Color rect_clipboard_color;
27 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
29 CURSOR_STYLE_RESIZE_VERT, // [1]
30 CURSOR_STYLE_RESIZE_HORIS, // [2]
31 CURSOR_STYLE_RESIZE_DIAG1, // [3]
32 CURSOR_STYLE_RESIZE_VERT, // [4]
34 CURSOR_STYLE_RESIZE_DIAG2, // [6]
36 CURSOR_STYLE_RESIZE_HORIS, // [8]
37 CURSOR_STYLE_RESIZE_DIAG2, // [9]
40 CURSOR_STYLE_RESIZE_DIAG1 // [12]
58 char id[ENTITY_MAX_ID_SIZE];
79 UndoElementContext element;
84 RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
87 trace_assert(index < layer->rects.count);
89 RectUndoContext undo_context;
90 undo_context.add.type = RECT_UNDO_ADD;
91 undo_context.add.layer = layer;
92 undo_context.add.index = index;
97 RectUndoContext create_rect_undo_element_context(RectLayer *layer)
100 size_t index = (size_t) layer->selection;
101 trace_assert(index < layer->rects.count);
103 RectUndoContext undo_context;
104 undo_context.element.layer = layer;
105 undo_context.element.index = index;
106 dynarray_copy_to(&layer->rects, &undo_context.element.rect, index);
107 dynarray_copy_to(&layer->colors, &undo_context.element.color, index);
108 dynarray_copy_to(&layer->ids, undo_context.element.id, index);
109 dynarray_copy_to(&layer->actions, &undo_context.element.action, index);
114 RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer)
116 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
117 undo_context.type = RECT_UNDO_UPDATE;
122 RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer)
124 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
125 undo_context.type = RECT_UNDO_DELETE;
130 RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
132 RectUndoContext undo_context;
133 undo_context.swap.type = RECT_UNDO_SWAP;
134 undo_context.swap.layer = rect_layer;
135 undo_context.swap.index1 = index1;
136 undo_context.swap.index2 = index2;
141 void rect_layer_undo(void *context, size_t context_size)
143 trace_assert(context);
144 trace_assert(sizeof(RectUndoContext) == context_size);
146 RectUndoContext *undo_context = context;
148 switch (undo_context->type) {
149 case RECT_UNDO_ADD: {
150 RectLayer *layer = undo_context->add.layer;
151 dynarray_delete_at(&layer->rects, undo_context->add.index);
152 dynarray_delete_at(&layer->colors, undo_context->add.index);
153 dynarray_delete_at(&layer->ids, undo_context->add.index);
154 dynarray_delete_at(&layer->actions, undo_context->add.index);
155 layer->selection = -1;
158 case RECT_UNDO_DELETE: {
159 RectLayer *layer = undo_context->element.layer;
160 dynarray_insert_before(&layer->rects, undo_context->element.index, &undo_context->element.rect);
161 dynarray_insert_before(&layer->colors, undo_context->element.index, &undo_context->element.color);
162 dynarray_insert_before(&layer->ids, undo_context->element.index, &undo_context->element.id);
163 dynarray_insert_before(&layer->actions, undo_context->element.index, &undo_context->element.action);
164 layer->selection = -1;
167 case RECT_UNDO_UPDATE: {
168 RectLayer *layer = undo_context->element.layer;
169 dynarray_replace_at(&layer->rects, undo_context->element.index, &undo_context->element.rect);
170 dynarray_replace_at(&layer->colors, undo_context->element.index, &undo_context->element.color);
171 dynarray_replace_at(&layer->ids, undo_context->element.index, &undo_context->element.id);
172 dynarray_replace_at(&layer->actions, undo_context->element.index, &undo_context->element.action);
175 case RECT_UNDO_SWAP: {
176 RectLayer *layer = undo_context->element.layer;
177 dynarray_swap(&layer->rects, undo_context->swap.index1, undo_context->swap.index2);
178 dynarray_swap(&layer->colors, undo_context->swap.index1, undo_context->swap.index2);
179 dynarray_swap(&layer->ids, undo_context->swap.index1, undo_context->swap.index2);
180 dynarray_swap(&layer->actions, undo_context->swap.index1, undo_context->swap.index2);
185 #define RECT_UNDO_PUSH(HISTORY, CONTEXT) \
187 RectUndoContext context = (CONTEXT); \
195 static int rect_layer_add_rect(RectLayer *layer,
198 UndoHistory *undo_history)
202 dynarray_push(&layer->rects, &rect);
203 dynarray_push(&layer->colors, &color);
205 char id[ENTITY_MAX_ID_SIZE];
206 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
207 layer->id_name_prefix,
208 layer->id_name_counter++);
209 dynarray_push(&layer->ids, id);
211 dynarray_push_empty(&layer->actions);
215 create_rect_undo_add_context(
217 layer->rects.count - 1));
222 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
226 int n = (int) layer->rects.count;
227 Rect *rects = (Rect*)layer->rects.data;
229 for (int i = n - 1; i >= 0; --i) {
230 if (rect_contains_point(rects[i], position)) {
238 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
239 UndoHistory *undo_history)
242 trace_assert(a < layer->rects.count);
243 trace_assert(b < layer->rects.count);
245 dynarray_swap(&layer->rects, a, b);
246 dynarray_swap(&layer->colors, a, b);
247 dynarray_swap(&layer->ids, a, b);
248 dynarray_swap(&layer->actions, a, b);
250 RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
253 static int rect_layer_delete_rect_at(RectLayer *layer,
255 UndoHistory *undo_history)
259 RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer));
261 dynarray_delete_at(&layer->rects, i);
262 dynarray_delete_at(&layer->colors, i);
263 dynarray_delete_at(&layer->ids, i);
264 dynarray_delete_at(&layer->actions, i);
269 static int calc_resize_mask(Vec2f point, Rect rect)
272 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
273 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
274 mask = mask | (1 << side);
280 static int rect_layer_event_idle(RectLayer *layer,
281 const SDL_Event *event,
282 const Camera *camera,
283 UndoHistory *undo_history)
287 trace_assert(camera);
289 int color_changed = 0;
290 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
295 if (layer->selection >= 0) {
296 dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
297 layer->state = RECT_LAYER_RECOLOR;
302 Rect *rects = (Rect*)layer->rects.data;
304 switch (event->type) {
305 case SDL_MOUSEBUTTONDOWN: {
306 switch (event->button.button) {
307 case SDL_BUTTON_LEFT: {
308 Vec2f position = camera_map_screen(
312 int rect_at_position =
313 rect_layer_rect_at(layer, position);
316 Color *colors = (Color*)layer->colors.data;
318 if (layer->selection >= 0 &&
319 layer->selection == rect_at_position &&
320 (layer->resize_mask = calc_resize_mask(
321 vec((float) event->button.x, (float)event->button.y),
322 camera_rect(camera, rects[layer->selection])))) {
323 layer->state = RECT_LAYER_RESIZE;
324 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
325 } else if (rect_at_position >= 0) {
326 layer->selection = rect_at_position;
327 layer->state = RECT_LAYER_MOVE;
328 layer->move_anchor = vec_sub(
331 rects[layer->selection].x,
332 rects[layer->selection].y));
333 layer->color_picker =
334 create_color_picker_from_rgba(colors[rect_at_position]);
335 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
337 layer->selection = rect_at_position;
339 if (layer->selection < 0) {
340 layer->state = RECT_LAYER_CREATE;
341 layer->create_begin = position;
342 layer->create_end = position;
349 case SDL_MOUSEMOTION: {
351 Vec2f position = camera_map_screen(
355 if (layer->selection >= 0 &&
356 layer->selection == rect_layer_rect_at(layer, position) &&
357 (resize_mask = calc_resize_mask(
358 vec((float) event->button.x, (float)event->button.y),
359 camera_rect(camera, rects[layer->selection])))) {
360 layer->cursor->style = resize_styles[resize_mask];
362 layer->cursor->style = CURSOR_STYLE_POINTER;
367 switch (event->key.keysym.sym) {
369 if ((event->key.keysym.mod & KMOD_SHIFT)
370 && (layer->selection >= 0)
371 && ((size_t)(layer->selection + 1) < layer->rects.count)) {
372 rect_layer_swap_elements(
374 (size_t) layer->selection,
375 (size_t) layer->selection + 1,
382 if ((event->key.keysym.mod & KMOD_SHIFT)
383 && (layer->selection > 0)
384 && ((size_t) layer->selection < layer->rects.count)) {
385 rect_layer_swap_elements(
387 (size_t) layer->selection,
388 (size_t) layer->selection - 1,
395 if (layer->selection >= 0) {
396 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
397 layer->selection = -1;
402 // TODO(#1171): there is no UI indication that we are in the snapping mode
403 layer->snapping_enabled = !layer->snapping_enabled;
407 if (layer->selection >= 0) {
408 const char *ids = (char*)layer->ids.data;
409 Color *colors = (Color*)layer->colors.data;
412 &layer->id_edit_field,
413 RECT_LAYER_ID_LABEL_SIZE,
414 color_invert(colors[layer->selection]));
416 layer->state = RECT_LAYER_ID_RENAME;
418 &layer->id_edit_field,
419 ids + layer->selection * ENTITY_MAX_ID_SIZE);
420 SDL_StartTextInput();
425 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
427 dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
428 dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
433 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
435 SDL_GetMouseState(&x, &y);
436 Vec2f position = camera_map_screen(camera, x, y);
440 rect(position.x, position.y,
441 rect_clipboard_rect.w, rect_clipboard_rect.h),
442 rect_clipboard_color,
453 static int rect_layer_event_create(RectLayer *layer,
454 const SDL_Event *event,
455 const Camera *camera,
456 UndoHistory *undo_history)
460 trace_assert(camera);
462 switch (event->type) {
463 case SDL_MOUSEBUTTONUP: {
464 switch (event->button.button) {
465 case SDL_BUTTON_LEFT: {
466 const Rect real_rect =
470 const float area = real_rect.w * real_rect.h;
472 if (area >= CREATE_AREA_THRESHOLD) {
476 color_picker_rgba(&layer->color_picker),
479 log_info("The area is too small %f. Such small box won't be created.\n", area);
481 layer->state = RECT_LAYER_IDLE;
486 case SDL_MOUSEMOTION: {
487 layer->create_end = camera_map_screen(
497 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
500 trace_assert(layer->selection >= 0);
503 if (!layer->snapping_enabled) return;
505 Rect *rects = (Rect*)layer->rects.data;
506 size_t rects_size = layer->rects.count;
508 for (size_t i = 0; i < rects_size; ++i) {
509 if (i == (size_t) layer->selection) continue;
511 const Rect b = rects[i];
513 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
514 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
517 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
518 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
523 static int rect_layer_event_resize(RectLayer *layer,
524 const SDL_Event *event,
525 const Camera *camera,
526 UndoHistory *undo_history)
530 trace_assert(camera);
531 trace_assert(layer->selection >= 0);
533 Rect *rects = (Rect*)layer->rects.data;
535 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
537 switch (event->type) {
538 case SDL_MOUSEMOTION: {
539 Vec2f position = camera_map_screen(
544 switch (layer->resize_mask) {
546 Rect a = rect(rects[layer->selection].x,
548 rects[layer->selection].w,
549 rects[layer->selection].h);
551 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
553 layer->inter_rect = rect_from_points(
555 rect_position2(rects[layer->selection]));
559 Rect a = rect(position.x,
560 rects[layer->selection].y,
561 rects[layer->selection].w,
562 rects[layer->selection].h);
564 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
566 layer->inter_rect = rect_from_points(
568 rect_position2(rects[layer->selection]));
571 case 3: { // TOP,LEFT
575 rects[layer->selection].w,
576 rects[layer->selection].h);
578 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
580 layer->inter_rect = rect_from_points(
582 rect_position2(rects[layer->selection]));
586 Rect a = rect(rects[layer->selection].x,
588 rects[layer->selection].w,
589 rects[layer->selection].h);
591 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
593 layer->inter_rect = rect_from_points(
594 rect_position(rects[layer->selection]),
595 vec(rects[layer->selection].x + rects[layer->selection].w,
599 case 6: { // BOTTOM,LEFT
603 rects[layer->selection].w,
604 -rects[layer->selection].h);
606 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
608 layer->inter_rect = rect_from_points(
609 vec(a.x, rects[layer->selection].y),
610 vec(rects[layer->selection].x + rects[layer->selection].w,
615 Rect a = rect(position.x,
616 rects[layer->selection].y,
617 rects[layer->selection].w,
618 rects[layer->selection].h);
620 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
622 layer->inter_rect = rect_from_points(
623 rect_position(rects[layer->selection]),
624 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
627 case 9: { // TOP,RIGHT
631 -rects[layer->selection].w,
632 rects[layer->selection].h);
634 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
636 layer->inter_rect = rect_from_points(
637 vec(rects[layer->selection].x, a.y),
639 rects[layer->selection].y + rects[layer->selection].h));
642 case 12: { // BOTTOM,RIGHT
646 -rects[layer->selection].w,
647 -rects[layer->selection].h);
649 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
651 layer->inter_rect = rect_from_points(
652 rect_position(rects[layer->selection]),
659 case SDL_MOUSEBUTTONUP: {
660 layer->state = RECT_LAYER_IDLE;
661 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
662 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
670 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
671 float snapping_threshold)
675 trace_assert(layer->selection >= 0);
677 if (!layer->snapping_enabled) return;
679 Rect *rects = (Rect*)layer->rects.data;
680 size_t rects_size = layer->rects.count;
682 for (size_t i = 0; i < rects_size; ++i) {
683 if (i == (size_t) layer->selection) continue;
685 const Rect b = rects[i];
687 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
688 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
691 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
692 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
697 static int rect_layer_event_move(RectLayer *layer,
698 const SDL_Event *event,
699 const Camera *camera,
700 UndoHistory *undo_history)
704 trace_assert(camera);
705 trace_assert(layer->selection >= 0);
707 Rect *rects = (Rect*)layer->rects.data;
709 switch (event->type) {
710 case SDL_MOUSEMOTION: {
711 const Uint8 *state = SDL_GetKeyboardState(NULL);
712 const Vec2f mouse_pos = vec_sub(
719 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
720 layer->inter_rect.x = mouse_pos.x;
721 layer->inter_rect.y = mouse_pos.y;
723 const Vec2f rect_pos = rect_position(rects[layer->selection]);
725 const float dx = fabsf(rect_pos.x - mouse_pos.x);
726 const float dy = fabsf(rect_pos.y - mouse_pos.y);
729 layer->inter_rect.x = mouse_pos.x;
730 layer->inter_rect.y = rect_pos.y;
732 layer->inter_rect.x = rect_pos.x;
733 layer->inter_rect.y = mouse_pos.y;
737 snap_rect_move_if_enabled(layer, &layer->inter_rect,
738 SNAPPING_THRESHOLD / camera->scale);
741 case SDL_MOUSEBUTTONUP: {
742 layer->state = RECT_LAYER_IDLE;
744 float distance = vec_length(
745 vec_sub(rect_position(layer->inter_rect),
746 rect_position(rects[layer->selection])));
748 if (distance > 1e-6) {
749 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
750 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
757 static int rect_layer_event_id_rename(RectLayer *layer,
758 const SDL_Event *event,
759 const Camera *camera,
760 UndoHistory *undo_history)
764 trace_assert(camera);
765 trace_assert(layer->selection >= 0);
767 switch (event->type) {
769 switch (event->key.keysym.sym) {
771 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
773 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
774 memset(id, 0, ENTITY_MAX_ID_SIZE);
775 memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
776 layer->state = RECT_LAYER_IDLE;
781 layer->state = RECT_LAYER_IDLE;
788 return edit_field_event(&layer->id_edit_field, event);
791 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
800 RectLayer create_rect_layer(const char *id_name_prefix, Cursor *cursor)
802 trace_assert(cursor);
804 RectLayer result = {0};
806 result.ids = create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE);
807 result.rects = create_dynarray(sizeof(Rect));
808 result.colors = create_dynarray(sizeof(Color));
809 result.actions = create_dynarray(sizeof(Action));
810 result.id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
811 result.id_edit_field.font_color = COLOR_BLACK;
812 result.color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
813 result.selection = -1;
814 result.id_name_prefix = id_name_prefix;
815 result.cursor = cursor;
820 RectLayer chop_rect_layer(Memory *memory,
822 const char *id_name_prefix,
825 trace_assert(memory);
828 RectLayer layer = create_rect_layer(id_name_prefix, cursor);
830 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
831 char id[ENTITY_MAX_ID_SIZE];
832 for (int i = 0; i < n; ++i) {
834 String line = trim(chop_by_delim(input, '\n'));
835 String string_id = trim(chop_word(&line));
836 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
837 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
838 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
839 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
840 Color color = hexs(trim(chop_word(&line)));
842 memset(id, 0, ENTITY_MAX_ID_SIZE);
846 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
848 dynarray_push(&layer.rects, &rect);
849 dynarray_push(&layer.colors, &color);
850 dynarray_push(&layer.ids, id);
857 String action_string = trim(chop_word(&line));
858 if (action_string.count > 0) {
859 action.type = (ActionType)atol(string_to_cstr(memory, action_string));
860 switch (action.type) {
861 case ACTION_NONE: break;
862 case ACTION_TOGGLE_GOAL:
863 case ACTION_HIDE_LABEL: {
864 String label_id = trim(chop_word(&line));
865 trace_assert(label_id.count > 0);
866 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
867 memcpy(action.entity_id,
870 ENTITY_MAX_ID_SIZE - 1,
874 case ACTION_N: break;
878 dynarray_push(&layer.actions, &action);
884 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
887 trace_assert(camera);
889 const size_t n = layer->rects.count;
890 Rect *rects = (Rect *)layer->rects.data;
891 Color *colors = (Color *)layer->colors.data;
892 const char *ids = (const char *)layer->ids.data;
895 for (size_t i = 0; i < n; ++i) {
896 Rect rect = rects[i];
897 Color color = colors[i];
899 if (layer->selection == (int) i) {
900 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
901 rect = layer->inter_rect;
904 if (layer->state == RECT_LAYER_RECOLOR) {
905 color = layer->inter_color;
910 if (camera_fill_rect(
915 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
921 if (active && layer->selection >= 0) {
922 Rect rect = rects[layer->selection];
923 Color color = colors[layer->selection];
925 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
926 rect = layer->inter_rect;
929 if (layer->state == RECT_LAYER_RECOLOR) {
930 color = layer->inter_color;
933 const Rect overlay_rect =
935 camera_rect(camera, rect),
936 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
937 const Color overlay_color = color_invert(color);
940 if (camera_draw_thicc_rect_screen(
944 RECT_LAYER_SELECTION_THICCNESS) < 0) {
948 const Vec2f rect_id_pos = vec_sub(
951 RECT_LAYER_ID_LABEL_SIZE,
952 vec(0.0f, FONT_CHAR_HEIGHT)));
955 if (layer->state == RECT_LAYER_ID_RENAME) {
956 // ID renaming Edit Field
957 if (edit_field_render_world(
958 &layer->id_edit_field,
965 if (camera_render_text(
967 ids + layer->selection * ENTITY_MAX_ID_SIZE,
968 RECT_LAYER_ID_LABEL_SIZE,
977 const Color color = color_picker_rgba(&layer->color_picker);
978 if (layer->state == RECT_LAYER_CREATE) {
979 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
984 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
992 int rect_layer_event_recolor(RectLayer *layer,
993 const SDL_Event *event,
994 const Camera *camera,
995 UndoHistory *undo_history)
999 trace_assert(camera);
1000 trace_assert(undo_history);
1001 trace_assert(layer->selection >= 0);
1003 int color_changed = 0;
1004 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1008 if (color_changed) {
1009 layer->inter_color = color_picker_rgba(&layer->color_picker);
1011 if (!color_picker_drag(&layer->color_picker)) {
1012 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
1013 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1014 layer->state = RECT_LAYER_IDLE;
1021 int rect_layer_event(RectLayer *layer,
1022 const SDL_Event *event,
1023 const Camera *camera,
1024 UndoHistory *undo_history)
1026 trace_assert(layer);
1027 trace_assert(event);
1028 trace_assert(undo_history);
1030 switch (layer->state) {
1031 case RECT_LAYER_IDLE:
1032 return rect_layer_event_idle(layer, event, camera, undo_history);
1034 case RECT_LAYER_CREATE:
1035 return rect_layer_event_create(layer, event, camera, undo_history);
1037 case RECT_LAYER_RESIZE:
1038 return rect_layer_event_resize(layer, event, camera, undo_history);
1040 case RECT_LAYER_MOVE:
1041 return rect_layer_event_move(layer, event, camera, undo_history);
1043 case RECT_LAYER_ID_RENAME:
1044 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1046 case RECT_LAYER_RECOLOR:
1047 return rect_layer_event_recolor(layer, event, camera, undo_history);
1054 size_t rect_layer_count(const RectLayer *layer)
1056 return layer->rects.count;
1059 const Rect *rect_layer_rects(const RectLayer *layer)
1061 return (const Rect *)layer->rects.data;
1064 const Color *rect_layer_colors(const RectLayer *layer)
1066 return (const Color *)layer->colors.data;
1069 const char *rect_layer_ids(const RectLayer *layer)
1071 return (const char *)layer->ids.data;
1074 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1076 trace_assert(layer);
1077 trace_assert(filedump);
1079 size_t n = layer->ids.count;
1080 char *ids = (char *)layer->ids.data;
1081 Rect *rects = (Rect *)layer->rects.data;
1082 Color *colors = (Color *)layer->colors.data;
1083 Action *actions = (Action *)layer->actions.data;
1085 fprintf(filedump, "%zd\n", n);
1086 for (size_t i = 0; i < n; ++i) {
1087 fprintf(filedump, "%s %f %f %f %f ",
1088 ids + ENTITY_MAX_ID_SIZE * i,
1089 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1090 color_hex_to_stream(colors[i], filedump);
1092 switch (actions[i].type) {
1093 case ACTION_NONE: {} break;
1095 case ACTION_TOGGLE_GOAL:
1096 case ACTION_HIDE_LABEL: {
1097 fprintf(filedump, " %d %.*s",
1098 (int)actions[i].type,
1099 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1101 case ACTION_N: break;
1104 fprintf(filedump, "\n");
1110 const Action *rect_layer_actions(const RectLayer *layer)
1112 return (const Action *)layer->actions.data;