4 #include "game/camera.h"
5 #include "system/stacktrace.h"
6 #include "system/nth_alloc.h"
7 #include "system/log.h"
10 #include "rect_layer.h"
12 #include "system/str.h"
13 #include "undo_history.h"
14 #include "game/level/action.h"
16 #include "math/extrema.h"
18 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
19 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
20 #define CREATE_AREA_THRESHOLD 10.0
22 static int rect_clipboard = 0;
23 static Rect rect_clipboard_rect;
24 static Color rect_clipboard_color;
26 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
28 CURSOR_STYLE_RESIZE_VERT, // [1]
29 CURSOR_STYLE_RESIZE_HORIS, // [2]
30 CURSOR_STYLE_RESIZE_DIAG1, // [3]
31 CURSOR_STYLE_RESIZE_VERT, // [4]
33 CURSOR_STYLE_RESIZE_DIAG2, // [6]
35 CURSOR_STYLE_RESIZE_HORIS, // [8]
36 CURSOR_STYLE_RESIZE_DIAG2, // [9]
39 CURSOR_STYLE_RESIZE_DIAG1 // [12]
57 char id[ENTITY_MAX_ID_SIZE];
78 UndoElementContext element;
83 RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
86 trace_assert(index < layer->rects.count);
88 RectUndoContext undo_context;
89 undo_context.add.type = RECT_UNDO_ADD;
90 undo_context.add.layer = layer;
91 undo_context.add.index = index;
96 RectUndoContext create_rect_undo_element_context(RectLayer *layer, size_t index)
99 trace_assert(index < layer->rects.count);
101 RectUndoContext undo_context;
102 undo_context.element.layer = layer;
103 undo_context.element.index = index;
104 dynarray_copy_to(&layer->rects, &undo_context.element.rect, index);
105 dynarray_copy_to(&layer->colors, &undo_context.element.color, index);
106 dynarray_copy_to(&layer->ids, undo_context.element.id, index);
107 dynarray_copy_to(&layer->actions, &undo_context.element.action, index);
112 RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer, size_t index)
114 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer, index);
115 undo_context.type = RECT_UNDO_UPDATE;
120 RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer, size_t i)
122 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer, i);
123 undo_context.type = RECT_UNDO_DELETE;
128 RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
130 RectUndoContext undo_context;
131 undo_context.swap.type = RECT_UNDO_SWAP;
132 undo_context.swap.layer = rect_layer;
133 undo_context.swap.index1 = index1;
134 undo_context.swap.index2 = index2;
139 void rect_layer_undo(void *context, size_t context_size)
141 trace_assert(context);
142 trace_assert(sizeof(RectUndoContext) == context_size);
144 RectUndoContext *undo_context = context;
146 switch (undo_context->type) {
147 case RECT_UNDO_ADD: {
148 RectLayer *layer = undo_context->add.layer;
149 dynarray_delete_at(&layer->rects, undo_context->add.index);
150 dynarray_delete_at(&layer->colors, undo_context->add.index);
151 dynarray_delete_at(&layer->ids, undo_context->add.index);
152 dynarray_delete_at(&layer->actions, undo_context->add.index);
153 layer->selection = -1;
156 case RECT_UNDO_DELETE: {
157 RectLayer *layer = undo_context->element.layer;
158 dynarray_insert_before(&layer->rects, undo_context->element.index, &undo_context->element.rect);
159 dynarray_insert_before(&layer->colors, undo_context->element.index, &undo_context->element.color);
160 dynarray_insert_before(&layer->ids, undo_context->element.index, &undo_context->element.id);
161 dynarray_insert_before(&layer->actions, undo_context->element.index, &undo_context->element.action);
162 layer->selection = -1;
165 case RECT_UNDO_UPDATE: {
166 RectLayer *layer = undo_context->element.layer;
167 dynarray_replace_at(&layer->rects, undo_context->element.index, &undo_context->element.rect);
168 dynarray_replace_at(&layer->colors, undo_context->element.index, &undo_context->element.color);
169 dynarray_replace_at(&layer->ids, undo_context->element.index, &undo_context->element.id);
170 dynarray_replace_at(&layer->actions, undo_context->element.index, &undo_context->element.action);
173 case RECT_UNDO_SWAP: {
174 RectLayer *layer = undo_context->element.layer;
175 dynarray_swap(&layer->rects, undo_context->swap.index1, undo_context->swap.index2);
176 dynarray_swap(&layer->colors, undo_context->swap.index1, undo_context->swap.index2);
177 dynarray_swap(&layer->ids, undo_context->swap.index1, undo_context->swap.index2);
178 dynarray_swap(&layer->actions, undo_context->swap.index1, undo_context->swap.index2);
183 #define RECT_UNDO_PUSH(HISTORY, CONTEXT) \
185 RectUndoContext context = (CONTEXT); \
193 static int rect_layer_add_rect(RectLayer *layer,
196 UndoHistory *undo_history)
200 dynarray_push(&layer->rects, &rect);
201 dynarray_push(&layer->colors, &color);
203 char id[ENTITY_MAX_ID_SIZE];
204 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
205 layer->id_name_prefix,
206 layer->id_name_counter++);
207 dynarray_push(&layer->ids, id);
209 dynarray_push_empty(&layer->actions);
213 create_rect_undo_add_context(
215 layer->rects.count - 1));
220 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
224 int n = (int) layer->rects.count;
225 Rect *rects = (Rect*)layer->rects.data;
227 for (int i = n - 1; i >= 0; --i) {
228 if (rect_contains_point(rects[i], position)) {
236 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
237 UndoHistory *undo_history)
240 trace_assert(a < layer->rects.count);
241 trace_assert(b < layer->rects.count);
243 dynarray_swap(&layer->rects, a, b);
244 dynarray_swap(&layer->colors, a, b);
245 dynarray_swap(&layer->ids, a, b);
246 dynarray_swap(&layer->actions, a, b);
248 RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
251 static int rect_layer_delete_rect_at_index(RectLayer *layer,
253 UndoHistory *undo_history)
257 RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer, i));
259 dynarray_delete_at(&layer->rects, i);
260 dynarray_delete_at(&layer->colors, i);
261 dynarray_delete_at(&layer->ids, i);
262 dynarray_delete_at(&layer->actions, i);
267 static int calc_resize_mask(Vec2f point, Rect rect)
270 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
271 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
272 mask = mask | (1 << side);
278 #define TOOL_BUTTON_WIDTH 50.0f
279 #define TOOL_BUTTON_HEIGHT 50.0f
280 #define TOOL_BAR_PADDING 20.0f
283 Rect subtract_tool_button_rect(const Camera *camera)
285 const Rect view_port = camera_view_port_screen(camera);
288 view_port.h - TOOL_BUTTON_HEIGHT - TOOL_BAR_PADDING,
294 Rect snapping_tool_button_rect(const Camera *camera)
296 const Rect view_port = camera_view_port_screen(camera);
298 TOOL_BAR_PADDING + TOOL_BUTTON_WIDTH + TOOL_BAR_PADDING,
299 view_port.h - TOOL_BUTTON_HEIGHT - TOOL_BAR_PADDING,
304 static int rect_layer_event_idle(RectLayer *layer,
305 const SDL_Event *event,
306 const Camera *camera,
307 UndoHistory *undo_history)
311 trace_assert(camera);
313 int color_changed = 0;
314 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
319 if (layer->selection >= 0) {
320 dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
321 layer->state = RECT_LAYER_RECOLOR;
326 Rect *rects = (Rect*)layer->rects.data;
328 switch (event->type) {
329 case SDL_MOUSEBUTTONDOWN: {
330 switch (event->button.button) {
331 case SDL_BUTTON_LEFT: {
332 Vec2f screen_position =
333 vec((float) event->button.x,
334 (float) event->button.y);
335 if (rect_contains_point(subtract_tool_button_rect(camera), screen_position)) {
336 layer->subtract_enabled = !layer->subtract_enabled;
340 if (rect_contains_point(snapping_tool_button_rect(camera), screen_position)) {
341 layer->snapping_enabled = !layer->snapping_enabled;
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 = layer->subtract_enabled
378 ? RECT_LAYER_SUBTRACT
380 layer->create_begin = position;
381 layer->create_end = position;
388 case SDL_MOUSEMOTION: {
390 Vec2f position = camera_map_screen(
394 if (layer->selection >= 0 &&
395 layer->selection == rect_layer_rect_at(layer, position) &&
396 (resize_mask = calc_resize_mask(
397 vec((float) event->button.x, (float)event->button.y),
398 camera_rect(camera, rects[layer->selection])))) {
399 layer->cursor->style = resize_styles[resize_mask];
401 layer->cursor->style = CURSOR_STYLE_POINTER;
406 switch (event->key.keysym.sym) {
408 if ((event->key.keysym.mod & KMOD_SHIFT)
409 && (layer->selection >= 0)
410 && ((size_t)(layer->selection + 1) < layer->rects.count)) {
411 rect_layer_swap_elements(
413 (size_t) layer->selection,
414 (size_t) layer->selection + 1,
421 if ((event->key.keysym.mod & KMOD_SHIFT)
422 && (layer->selection > 0)
423 && ((size_t) layer->selection < layer->rects.count)) {
424 rect_layer_swap_elements(
426 (size_t) layer->selection,
427 (size_t) layer->selection - 1,
434 if (layer->selection >= 0) {
435 rect_layer_delete_rect_at_index(
437 (size_t) layer->selection,
439 layer->selection = -1;
444 // TODO(#1171): there is no UI indication that we are in the snapping mode
445 layer->snapping_enabled = !layer->snapping_enabled;
449 if (layer->selection >= 0) {
450 const char *ids = (char*)layer->ids.data;
451 Color *colors = (Color*)layer->colors.data;
454 &layer->id_edit_field,
455 RECT_LAYER_ID_LABEL_SIZE,
456 color_invert(colors[layer->selection]));
458 layer->state = RECT_LAYER_ID_RENAME;
460 &layer->id_edit_field,
461 ids + layer->selection * ENTITY_MAX_ID_SIZE);
462 SDL_StartTextInput();
467 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
469 dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
470 dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
475 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
477 SDL_GetMouseState(&x, &y);
478 Vec2f position = camera_map_screen(camera, x, y);
482 rect(position.x, position.y,
483 rect_clipboard_rect.w, rect_clipboard_rect.h),
484 rect_clipboard_color,
495 #define GEOMETRY_CAPACITY 256
500 Rect rects[GEOMETRY_CAPACITY];
501 Color colors[GEOMETRY_CAPACITY];
505 void push_geometry(Geometry *geometry, Rect rect, Color color)
508 // TODO(#1252): push_geometry may fail if there is too many rects produced
509 assert(geometry->count < GEOMETRY_CAPACITY);
511 if ((rect.w * rect.h) > 1e-6f) {
512 size_t i = (geometry->first + geometry->count) % GEOMETRY_CAPACITY;
513 geometry->rects[i] = rect;
514 geometry->colors[i] = color;
520 void subtract_rect_from_rect(Rect a, Color color_a, Rect c, Geometry *result)
524 Rect b = rects_overlap_area(a, c);
526 if (b.w * b.h < 1e-6) {
527 push_geometry(result, a, color_a);
531 push_geometry(result, (Rect) {a.x, a.y, a.w, b.y - a.y}, color_a);
532 push_geometry(result, (Rect) {a.x, b.y, b.x - a.x, b.h}, color_a);
533 push_geometry(result, (Rect) {
536 a.w - (b.x - a.x) - b.w,
539 push_geometry(result, (Rect) {
543 a.h - (b.y - a.y) - b.h
548 void subtract_rect_from_geometry(Geometry *result, Rect b)
552 size_t count = result->count;
553 size_t first = result->first;
555 for (size_t i = 0; i < count; ++i) {
556 result->first = (result->first + 1) % GEOMETRY_CAPACITY;
558 subtract_rect_from_rect(
559 result->rects[(i + first) % GEOMETRY_CAPACITY],
560 result->colors[(i + first) % GEOMETRY_CAPACITY],
566 static int rect_layer_event_subtract(RectLayer *layer,
567 const SDL_Event *event,
568 const Camera *camera,
569 UndoHistory *undo_history)
573 trace_assert(camera);
574 trace_assert(undo_history);
576 Rect *rects = layer->rects.data;
577 Color *colors = layer->colors.data;
579 switch (event->type) {
580 case SDL_MOUSEBUTTONUP: {
581 switch (event->button.button) {
582 case SDL_BUTTON_LEFT: {
583 const Rect real_rect =
587 const float area = real_rect.w * real_rect.h;
589 Geometry geometry = {0};
591 if (area >= CREATE_AREA_THRESHOLD) {
592 for (size_t i = 0; i < layer->rects.count;) {
593 Rect overlap_area = rects_overlap_area(
596 if (overlap_area.w * overlap_area.h > 1e-6) {
597 push_geometry(&geometry, rects[i], colors[i]);
598 rect_layer_delete_rect_at_index(layer, i, undo_history);
604 subtract_rect_from_geometry(&geometry, real_rect);
606 for (size_t i = 0; i < geometry.count; ++i) {
607 size_t j = (i + geometry.first) % GEOMETRY_CAPACITY;
615 log_info("The area is too small %f. Such small box won't be cut.\n", area);
617 layer->state = RECT_LAYER_IDLE;
622 case SDL_MOUSEMOTION: {
623 layer->create_end = camera_map_screen(
633 static int rect_layer_event_create(RectLayer *layer,
634 const SDL_Event *event,
635 const Camera *camera,
636 UndoHistory *undo_history)
640 trace_assert(camera);
642 switch (event->type) {
643 case SDL_MOUSEBUTTONUP: {
644 switch (event->button.button) {
645 case SDL_BUTTON_LEFT: {
646 const Rect real_rect =
650 const float area = real_rect.w * real_rect.h;
652 if (area >= CREATE_AREA_THRESHOLD) {
656 color_picker_rgba(&layer->color_picker),
659 log_info("The area is too small %f. Such small box won't be created.\n", area);
661 layer->state = RECT_LAYER_IDLE;
666 case SDL_MOUSEMOTION: {
667 layer->create_end = camera_map_screen(
677 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
680 trace_assert(layer->selection >= 0);
683 if (!layer->snapping_enabled) return;
685 Rect *rects = (Rect*)layer->rects.data;
686 size_t rects_size = layer->rects.count;
688 for (size_t i = 0; i < rects_size; ++i) {
689 if (i == (size_t) layer->selection) continue;
691 const Rect b = rects[i];
693 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
694 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
697 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
698 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
703 static int rect_layer_event_resize(RectLayer *layer,
704 const SDL_Event *event,
705 const Camera *camera,
706 UndoHistory *undo_history)
710 trace_assert(camera);
711 trace_assert(layer->selection >= 0);
713 Rect *rects = (Rect*)layer->rects.data;
715 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
717 switch (event->type) {
718 case SDL_MOUSEMOTION: {
719 Vec2f position = camera_map_screen(
724 switch (layer->resize_mask) {
726 Rect a = rect(rects[layer->selection].x,
728 rects[layer->selection].w,
729 rects[layer->selection].h);
731 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
733 layer->inter_rect = rect_from_points(
735 rect_position2(rects[layer->selection]));
739 Rect a = rect(position.x,
740 rects[layer->selection].y,
741 rects[layer->selection].w,
742 rects[layer->selection].h);
744 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
746 layer->inter_rect = rect_from_points(
748 rect_position2(rects[layer->selection]));
751 case 3: { // TOP,LEFT
755 rects[layer->selection].w,
756 rects[layer->selection].h);
758 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
760 layer->inter_rect = rect_from_points(
762 rect_position2(rects[layer->selection]));
766 Rect a = rect(rects[layer->selection].x,
768 rects[layer->selection].w,
769 rects[layer->selection].h);
771 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
773 layer->inter_rect = rect_from_points(
774 rect_position(rects[layer->selection]),
775 vec(rects[layer->selection].x + rects[layer->selection].w,
779 case 6: { // BOTTOM,LEFT
783 rects[layer->selection].w,
784 -rects[layer->selection].h);
786 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
788 layer->inter_rect = rect_from_points(
789 vec(a.x, rects[layer->selection].y),
790 vec(rects[layer->selection].x + rects[layer->selection].w,
795 Rect a = rect(position.x,
796 rects[layer->selection].y,
797 rects[layer->selection].w,
798 rects[layer->selection].h);
800 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
802 layer->inter_rect = rect_from_points(
803 rect_position(rects[layer->selection]),
804 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
807 case 9: { // TOP,RIGHT
811 -rects[layer->selection].w,
812 rects[layer->selection].h);
814 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
816 layer->inter_rect = rect_from_points(
817 vec(rects[layer->selection].x, a.y),
819 rects[layer->selection].y + rects[layer->selection].h));
822 case 12: { // BOTTOM,RIGHT
826 -rects[layer->selection].w,
827 -rects[layer->selection].h);
829 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
831 layer->inter_rect = rect_from_points(
832 rect_position(rects[layer->selection]),
839 case SDL_MOUSEBUTTONUP: {
840 layer->state = RECT_LAYER_IDLE;
843 create_rect_undo_update_context(
845 (size_t) layer->selection));
846 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
854 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
855 float snapping_threshold)
859 trace_assert(layer->selection >= 0);
861 if (!layer->snapping_enabled) return;
863 Rect *rects = (Rect*)layer->rects.data;
864 size_t rects_size = layer->rects.count;
866 for (size_t i = 0; i < rects_size; ++i) {
867 if (i == (size_t) layer->selection) continue;
869 const Rect b = rects[i];
871 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
872 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
875 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
876 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
881 static int rect_layer_event_move(RectLayer *layer,
882 const SDL_Event *event,
883 const Camera *camera,
884 UndoHistory *undo_history)
888 trace_assert(camera);
889 trace_assert(layer->selection >= 0);
891 Rect *rects = (Rect*)layer->rects.data;
893 switch (event->type) {
894 case SDL_MOUSEMOTION: {
895 const Uint8 *state = SDL_GetKeyboardState(NULL);
896 const Vec2f mouse_pos = vec_sub(
903 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
904 layer->inter_rect.x = mouse_pos.x;
905 layer->inter_rect.y = mouse_pos.y;
907 const Vec2f rect_pos = rect_position(rects[layer->selection]);
909 const float dx = fabsf(rect_pos.x - mouse_pos.x);
910 const float dy = fabsf(rect_pos.y - mouse_pos.y);
913 layer->inter_rect.x = mouse_pos.x;
914 layer->inter_rect.y = rect_pos.y;
916 layer->inter_rect.x = rect_pos.x;
917 layer->inter_rect.y = mouse_pos.y;
921 snap_rect_move_if_enabled(layer, &layer->inter_rect,
922 SNAPPING_THRESHOLD / camera->scale);
925 case SDL_MOUSEBUTTONUP: {
926 layer->state = RECT_LAYER_IDLE;
928 float distance = vec_length(
929 vec_sub(rect_position(layer->inter_rect),
930 rect_position(rects[layer->selection])));
932 if (distance > 1e-6) {
935 create_rect_undo_update_context(
936 layer, (size_t) layer->selection));
939 (size_t) layer->selection,
947 static int rect_layer_event_id_rename(RectLayer *layer,
948 const SDL_Event *event,
949 const Camera *camera,
950 UndoHistory *undo_history)
954 trace_assert(camera);
955 trace_assert(layer->selection >= 0);
957 switch (event->type) {
959 switch (event->key.keysym.sym) {
963 create_rect_undo_update_context(
965 (size_t) layer->selection));
967 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
968 memset(id, 0, ENTITY_MAX_ID_SIZE);
969 memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
970 layer->state = RECT_LAYER_IDLE;
975 layer->state = RECT_LAYER_IDLE;
982 return edit_field_event(&layer->id_edit_field, event);
985 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
994 RectLayer *create_rect_layer(Memory *memory,
995 const char *id_name_prefix,
998 trace_assert(memory);
999 trace_assert(id_name_prefix);
1000 trace_assert(cursor);
1002 RectLayer *rect_layer = memory_alloc(memory, sizeof(RectLayer));
1004 rect_layer->ids = create_dynarray(memory, sizeof(char) * ENTITY_MAX_ID_SIZE);
1005 rect_layer->rects = create_dynarray(memory, sizeof(Rect));
1006 rect_layer->colors = create_dynarray(memory, sizeof(Color));
1007 rect_layer->actions = create_dynarray(memory, sizeof(Action));
1008 rect_layer->id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
1009 rect_layer->id_edit_field.font_color = COLOR_BLACK;
1010 rect_layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
1011 rect_layer->selection = -1;
1012 rect_layer->id_name_prefix = id_name_prefix;
1013 rect_layer->cursor = cursor;
1018 void rect_layer_load(RectLayer *layer, Memory *memory, String *input)
1020 trace_assert(layer);
1021 trace_assert(memory);
1022 trace_assert(input);
1024 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
1025 char id[ENTITY_MAX_ID_SIZE];
1026 for (int i = 0; i < n; ++i) {
1028 String line = trim(chop_by_delim(input, '\n'));
1029 String string_id = trim(chop_word(&line));
1030 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1031 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1032 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1033 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1034 Color color = hexs(trim(chop_word(&line)));
1036 memset(id, 0, ENTITY_MAX_ID_SIZE);
1040 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
1042 dynarray_push(&layer->rects, &rect);
1043 dynarray_push(&layer->colors, &color);
1044 dynarray_push(&layer->ids, id);
1047 .type = ACTION_NONE,
1051 String action_string = trim(chop_word(&line));
1052 if (action_string.count > 0) {
1053 action.type = (ActionType)atol(string_to_cstr(memory, action_string));
1054 switch (action.type) {
1055 case ACTION_NONE: break;
1056 case ACTION_TOGGLE_GOAL:
1057 case ACTION_HIDE_LABEL: {
1058 String label_id = trim(chop_word(&line));
1059 trace_assert(label_id.count > 0);
1060 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
1061 memcpy(action.entity_id,
1064 ENTITY_MAX_ID_SIZE - 1,
1068 case ACTION_N: break;
1072 dynarray_push(&layer->actions, &action);
1076 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
1078 trace_assert(layer);
1079 trace_assert(camera);
1081 const size_t n = layer->rects.count;
1082 Rect *rects = (Rect *)layer->rects.data;
1083 Color *colors = (Color *)layer->colors.data;
1084 const char *ids = (const char *)layer->ids.data;
1087 for (size_t i = 0; i < n; ++i) {
1088 Rect rect = rects[i];
1089 Color color = colors[i];
1091 if (layer->selection == (int) i) {
1092 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1093 rect = layer->inter_rect;
1096 if (layer->state == RECT_LAYER_RECOLOR) {
1097 color = layer->inter_color;
1102 if (camera_fill_rect(
1107 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
1112 // Selection Overlay
1113 if (active && layer->selection >= 0) {
1114 Rect rect = rects[layer->selection];
1115 Color color = colors[layer->selection];
1117 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1118 rect = layer->inter_rect;
1121 if (layer->state == RECT_LAYER_RECOLOR) {
1122 color = layer->inter_color;
1125 const Rect overlay_rect =
1127 camera_rect(camera, rect),
1128 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1129 const Color overlay_color = color_invert(color);
1132 if (camera_draw_thicc_rect_screen(
1136 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1140 const Vec2f rect_id_pos = vec_sub(
1141 rect_position(rect),
1143 RECT_LAYER_ID_LABEL_SIZE,
1144 vec(0.0f, FONT_CHAR_HEIGHT)));
1147 if (layer->state == RECT_LAYER_ID_RENAME) {
1148 // ID renaming Edit Field
1149 if (edit_field_render_world(
1150 &layer->id_edit_field,
1157 if (camera_render_text(
1159 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1160 RECT_LAYER_ID_LABEL_SIZE,
1161 color_invert(color),
1169 const Color color = color_picker_rgba(&layer->color_picker);
1170 if (layer->state == RECT_LAYER_CREATE) {
1171 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1176 if (layer->state == RECT_LAYER_SUBTRACT) {
1177 if (camera_draw_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1182 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1188 // TODO(#1251): subtract and snapping tools don't have any icons
1189 camera_fill_rect_screen(
1191 subtract_tool_button_rect(camera),
1192 layer->subtract_enabled ? COLOR_RED : rgba(0.2f, 0.2f, 0.2f, 1.0f));
1193 camera_fill_rect_screen(
1195 snapping_tool_button_rect(camera),
1196 layer->snapping_enabled ? COLOR_RED : rgba(0.2f, 0.2f, 0.2f, 1.0f));
1203 int rect_layer_event_recolor(RectLayer *layer,
1204 const SDL_Event *event,
1205 const Camera *camera,
1206 UndoHistory *undo_history)
1208 trace_assert(layer);
1209 trace_assert(event);
1210 trace_assert(camera);
1211 trace_assert(undo_history);
1212 trace_assert(layer->selection >= 0);
1214 int color_changed = 0;
1215 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1219 if (color_changed) {
1220 layer->inter_color = color_picker_rgba(&layer->color_picker);
1222 if (!color_picker_drag(&layer->color_picker)) {
1225 create_rect_undo_update_context(
1227 (size_t)layer->selection));
1228 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1229 layer->state = RECT_LAYER_IDLE;
1236 int rect_layer_event(RectLayer *layer,
1237 const SDL_Event *event,
1238 const Camera *camera,
1239 UndoHistory *undo_history)
1241 trace_assert(layer);
1242 trace_assert(event);
1243 trace_assert(undo_history);
1245 switch (layer->state) {
1246 case RECT_LAYER_IDLE:
1247 return rect_layer_event_idle(layer, event, camera, undo_history);
1249 case RECT_LAYER_CREATE:
1250 return rect_layer_event_create(layer, event, camera, undo_history);
1252 case RECT_LAYER_RESIZE:
1253 return rect_layer_event_resize(layer, event, camera, undo_history);
1255 case RECT_LAYER_MOVE:
1256 return rect_layer_event_move(layer, event, camera, undo_history);
1258 case RECT_LAYER_ID_RENAME:
1259 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1261 case RECT_LAYER_RECOLOR:
1262 return rect_layer_event_recolor(layer, event, camera, undo_history);
1264 case RECT_LAYER_SUBTRACT:
1265 return rect_layer_event_subtract(layer, event, camera, undo_history);
1272 size_t rect_layer_count(const RectLayer *layer)
1274 return layer->rects.count;
1277 const Rect *rect_layer_rects(const RectLayer *layer)
1279 return (const Rect *)layer->rects.data;
1282 const Color *rect_layer_colors(const RectLayer *layer)
1284 return (const Color *)layer->colors.data;
1287 const char *rect_layer_ids(const RectLayer *layer)
1289 return (const char *)layer->ids.data;
1292 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1294 trace_assert(layer);
1295 trace_assert(filedump);
1297 size_t n = layer->ids.count;
1298 char *ids = (char *)layer->ids.data;
1299 Rect *rects = (Rect *)layer->rects.data;
1300 Color *colors = (Color *)layer->colors.data;
1301 Action *actions = (Action *)layer->actions.data;
1303 fprintf(filedump, "%zd\n", n);
1304 for (size_t i = 0; i < n; ++i) {
1305 fprintf(filedump, "%s %f %f %f %f ",
1306 ids + ENTITY_MAX_ID_SIZE * i,
1307 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1308 color_hex_to_stream(colors[i], filedump);
1310 switch (actions[i].type) {
1311 case ACTION_NONE: {} break;
1313 case ACTION_TOGGLE_GOAL:
1314 case ACTION_HIDE_LABEL: {
1315 fprintf(filedump, " %d %.*s",
1316 (int)actions[i].type,
1317 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1319 case ACTION_N: break;
1322 fprintf(filedump, "\n");
1328 const Action *rect_layer_actions(const RectLayer *layer)
1330 return (const Action *)layer->actions.data;