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
281 #define TOOL_BAR_BACKGROUND rgba(0.8f, 0.8f, 0.8f, 1.0f)
282 #define TOOL_BAR_FOREGROUND rgba(0.2f, 0.2f, 0.2f, 1.0f)
285 Rect subtract_tool_button_rect(const Camera *camera)
287 const Rect view_port = camera_view_port_screen(camera);
290 view_port.h - TOOL_BUTTON_HEIGHT - TOOL_BAR_PADDING,
296 Rect snapping_tool_button_rect(const Camera *camera)
298 const Rect view_port = camera_view_port_screen(camera);
300 TOOL_BAR_PADDING + TOOL_BUTTON_WIDTH + TOOL_BAR_PADDING,
301 view_port.h - TOOL_BUTTON_HEIGHT - TOOL_BAR_PADDING,
306 static int rect_layer_event_idle(RectLayer *layer,
307 const SDL_Event *event,
308 const Camera *camera,
309 UndoHistory *undo_history)
313 trace_assert(camera);
315 int color_changed = 0;
316 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
321 if (layer->selection >= 0) {
322 dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
323 layer->state = RECT_LAYER_RECOLOR;
328 Rect *rects = (Rect*)layer->rects.data;
330 switch (event->type) {
331 case SDL_MOUSEBUTTONDOWN: {
332 switch (event->button.button) {
333 case SDL_BUTTON_LEFT: {
334 Vec2f screen_position =
335 vec((float) event->button.x,
336 (float) event->button.y);
337 if (rect_contains_point(subtract_tool_button_rect(camera), screen_position)) {
338 layer->subtract_enabled = !layer->subtract_enabled;
342 if (rect_contains_point(snapping_tool_button_rect(camera), screen_position)) {
343 layer->snapping_enabled = !layer->snapping_enabled;
347 Vec2f position = camera_map_screen(
351 int rect_at_position =
352 rect_layer_rect_at(layer, position);
355 Color *colors = (Color*)layer->colors.data;
357 if (layer->subtract_enabled) {
358 layer->state = RECT_LAYER_SUBTRACT;
359 layer->create_begin = position;
360 layer->create_end = position;
361 } else if (layer->selection >= 0 &&
362 layer->selection == rect_at_position &&
363 (layer->resize_mask = calc_resize_mask(
364 vec((float) event->button.x, (float)event->button.y),
365 camera_rect(camera, rects[layer->selection])))) {
366 layer->state = RECT_LAYER_RESIZE;
367 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
368 } else if (rect_at_position >= 0) {
369 layer->selection = rect_at_position;
370 layer->state = RECT_LAYER_MOVE;
371 layer->move_anchor = vec_sub(
374 rects[layer->selection].x,
375 rects[layer->selection].y));
376 layer->color_picker =
377 create_color_picker_from_rgba(colors[rect_at_position]);
378 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
380 layer->selection = rect_at_position;
382 if (layer->selection < 0) {
383 layer->state = RECT_LAYER_CREATE;
384 layer->create_begin = position;
385 layer->create_end = position;
392 case SDL_MOUSEMOTION: {
394 Vec2f position = camera_map_screen(
398 if (layer->selection >= 0 &&
399 layer->selection == rect_layer_rect_at(layer, position) &&
400 (resize_mask = calc_resize_mask(
401 vec((float) event->button.x, (float)event->button.y),
402 camera_rect(camera, rects[layer->selection])))) {
403 layer->cursor->style = resize_styles[resize_mask];
405 layer->cursor->style = CURSOR_STYLE_POINTER;
410 switch (event->key.keysym.sym) {
412 if ((event->key.keysym.mod & KMOD_SHIFT)
413 && (layer->selection >= 0)
414 && ((size_t)(layer->selection + 1) < layer->rects.count)) {
415 rect_layer_swap_elements(
417 (size_t) layer->selection,
418 (size_t) layer->selection + 1,
425 if ((event->key.keysym.mod & KMOD_SHIFT)
426 && (layer->selection > 0)
427 && ((size_t) layer->selection < layer->rects.count)) {
428 rect_layer_swap_elements(
430 (size_t) layer->selection,
431 (size_t) layer->selection - 1,
438 if (layer->selection >= 0) {
439 rect_layer_delete_rect_at_index(
441 (size_t) layer->selection,
443 layer->selection = -1;
448 // TODO(#1171): there is no UI indication that we are in the snapping mode
449 layer->snapping_enabled = !layer->snapping_enabled;
453 if (layer->selection >= 0) {
454 const char *ids = (char*)layer->ids.data;
455 Color *colors = (Color*)layer->colors.data;
458 &layer->id_edit_field,
459 RECT_LAYER_ID_LABEL_SIZE,
460 color_invert(colors[layer->selection]));
462 layer->state = RECT_LAYER_ID_RENAME;
464 &layer->id_edit_field,
465 ids + layer->selection * ENTITY_MAX_ID_SIZE);
466 SDL_StartTextInput();
471 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
473 dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
474 dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
479 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
481 SDL_GetMouseState(&x, &y);
482 Vec2f position = camera_map_screen(camera, x, y);
486 rect(position.x, position.y,
487 rect_clipboard_rect.w, rect_clipboard_rect.h),
488 rect_clipboard_color,
499 #define GEOMETRY_CAPACITY 256
504 Rect rects[GEOMETRY_CAPACITY];
505 Color colors[GEOMETRY_CAPACITY];
509 void push_geometry(Geometry *geometry, Rect rect, Color color)
512 // TODO(#1252): push_geometry may fail if there is too many rects produced
513 assert(geometry->count < GEOMETRY_CAPACITY);
515 if ((rect.w * rect.h) > 1e-6f) {
516 size_t i = (geometry->first + geometry->count) % GEOMETRY_CAPACITY;
517 geometry->rects[i] = rect;
518 geometry->colors[i] = color;
524 void subtract_rect_from_rect(Rect a, Color color_a, Rect c, Geometry *result)
528 Rect b = rects_overlap_area(a, c);
530 if (b.w * b.h < 1e-6) {
531 push_geometry(result, a, color_a);
535 push_geometry(result, (Rect) {a.x, a.y, a.w, b.y - a.y}, color_a);
536 push_geometry(result, (Rect) {a.x, b.y, b.x - a.x, b.h}, color_a);
537 push_geometry(result, (Rect) {
540 a.w - (b.x - a.x) - b.w,
543 push_geometry(result, (Rect) {
547 a.h - (b.y - a.y) - b.h
552 void subtract_rect_from_geometry(Geometry *result, Rect b)
556 size_t count = result->count;
557 size_t first = result->first;
559 for (size_t i = 0; i < count; ++i) {
560 result->first = (result->first + 1) % GEOMETRY_CAPACITY;
562 subtract_rect_from_rect(
563 result->rects[(i + first) % GEOMETRY_CAPACITY],
564 result->colors[(i + first) % GEOMETRY_CAPACITY],
570 static int rect_layer_event_subtract(RectLayer *layer,
571 const SDL_Event *event,
572 const Camera *camera,
573 UndoHistory *undo_history)
577 trace_assert(camera);
578 trace_assert(undo_history);
580 Rect *rects = layer->rects.data;
581 Color *colors = layer->colors.data;
583 switch (event->type) {
584 case SDL_MOUSEBUTTONUP: {
585 switch (event->button.button) {
586 case SDL_BUTTON_LEFT: {
587 const Rect real_rect =
591 const float area = real_rect.w * real_rect.h;
593 Geometry geometry = {0};
595 if (area >= CREATE_AREA_THRESHOLD) {
596 for (size_t i = 0; i < layer->rects.count;) {
597 Rect overlap_area = rects_overlap_area(
600 if (overlap_area.w * overlap_area.h > 1e-6) {
601 push_geometry(&geometry, rects[i], colors[i]);
602 rect_layer_delete_rect_at_index(layer, i, undo_history);
608 subtract_rect_from_geometry(&geometry, real_rect);
610 for (size_t i = 0; i < geometry.count; ++i) {
611 size_t j = (i + geometry.first) % GEOMETRY_CAPACITY;
619 log_info("The area is too small %f. Such small box won't be cut.\n", area);
621 layer->state = RECT_LAYER_IDLE;
626 case SDL_MOUSEMOTION: {
627 layer->create_end = camera_map_screen(
637 static int rect_layer_event_create(RectLayer *layer,
638 const SDL_Event *event,
639 const Camera *camera,
640 UndoHistory *undo_history)
644 trace_assert(camera);
646 switch (event->type) {
647 case SDL_MOUSEBUTTONUP: {
648 switch (event->button.button) {
649 case SDL_BUTTON_LEFT: {
650 const Rect real_rect =
654 const float area = real_rect.w * real_rect.h;
656 if (area >= CREATE_AREA_THRESHOLD) {
660 color_picker_rgba(&layer->color_picker),
663 log_info("The area is too small %f. Such small box won't be created.\n", area);
665 layer->state = RECT_LAYER_IDLE;
670 case SDL_MOUSEMOTION: {
671 layer->create_end = camera_map_screen(
681 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
684 trace_assert(layer->selection >= 0);
687 if (!layer->snapping_enabled) return;
689 Rect *rects = (Rect*)layer->rects.data;
690 size_t rects_size = layer->rects.count;
692 for (size_t i = 0; i < rects_size; ++i) {
693 if (i == (size_t) layer->selection) continue;
695 const Rect b = rects[i];
697 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
698 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
701 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
702 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
707 static int rect_layer_event_resize(RectLayer *layer,
708 const SDL_Event *event,
709 const Camera *camera,
710 UndoHistory *undo_history)
714 trace_assert(camera);
715 trace_assert(layer->selection >= 0);
717 Rect *rects = (Rect*)layer->rects.data;
719 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
721 switch (event->type) {
722 case SDL_MOUSEMOTION: {
723 Vec2f position = camera_map_screen(
728 switch (layer->resize_mask) {
730 Rect a = rect(rects[layer->selection].x,
732 rects[layer->selection].w,
733 rects[layer->selection].h);
735 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
737 layer->inter_rect = rect_from_points(
739 rect_position2(rects[layer->selection]));
743 Rect a = rect(position.x,
744 rects[layer->selection].y,
745 rects[layer->selection].w,
746 rects[layer->selection].h);
748 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
750 layer->inter_rect = rect_from_points(
752 rect_position2(rects[layer->selection]));
755 case 3: { // TOP,LEFT
759 rects[layer->selection].w,
760 rects[layer->selection].h);
762 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
764 layer->inter_rect = rect_from_points(
766 rect_position2(rects[layer->selection]));
770 Rect a = rect(rects[layer->selection].x,
772 rects[layer->selection].w,
773 rects[layer->selection].h);
775 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
777 layer->inter_rect = rect_from_points(
778 rect_position(rects[layer->selection]),
779 vec(rects[layer->selection].x + rects[layer->selection].w,
783 case 6: { // BOTTOM,LEFT
787 rects[layer->selection].w,
788 -rects[layer->selection].h);
790 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
792 layer->inter_rect = rect_from_points(
793 vec(a.x, rects[layer->selection].y),
794 vec(rects[layer->selection].x + rects[layer->selection].w,
799 Rect a = rect(position.x,
800 rects[layer->selection].y,
801 rects[layer->selection].w,
802 rects[layer->selection].h);
804 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
806 layer->inter_rect = rect_from_points(
807 rect_position(rects[layer->selection]),
808 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
811 case 9: { // TOP,RIGHT
815 -rects[layer->selection].w,
816 rects[layer->selection].h);
818 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
820 layer->inter_rect = rect_from_points(
821 vec(rects[layer->selection].x, a.y),
823 rects[layer->selection].y + rects[layer->selection].h));
826 case 12: { // BOTTOM,RIGHT
830 -rects[layer->selection].w,
831 -rects[layer->selection].h);
833 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
835 layer->inter_rect = rect_from_points(
836 rect_position(rects[layer->selection]),
843 case SDL_MOUSEBUTTONUP: {
844 layer->state = RECT_LAYER_IDLE;
847 create_rect_undo_update_context(
849 (size_t) layer->selection));
850 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
858 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
859 float snapping_threshold)
863 trace_assert(layer->selection >= 0);
865 if (!layer->snapping_enabled) return;
867 Rect *rects = (Rect*)layer->rects.data;
868 size_t rects_size = layer->rects.count;
870 for (size_t i = 0; i < rects_size; ++i) {
871 if (i == (size_t) layer->selection) continue;
873 const Rect b = rects[i];
875 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
876 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
879 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
880 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
885 static int rect_layer_event_move(RectLayer *layer,
886 const SDL_Event *event,
887 const Camera *camera,
888 UndoHistory *undo_history)
892 trace_assert(camera);
893 trace_assert(layer->selection >= 0);
895 Rect *rects = (Rect*)layer->rects.data;
897 switch (event->type) {
898 case SDL_MOUSEMOTION: {
899 const Uint8 *state = SDL_GetKeyboardState(NULL);
900 const Vec2f mouse_pos = vec_sub(
907 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
908 layer->inter_rect.x = mouse_pos.x;
909 layer->inter_rect.y = mouse_pos.y;
911 const Vec2f rect_pos = rect_position(rects[layer->selection]);
913 const float dx = fabsf(rect_pos.x - mouse_pos.x);
914 const float dy = fabsf(rect_pos.y - mouse_pos.y);
917 layer->inter_rect.x = mouse_pos.x;
918 layer->inter_rect.y = rect_pos.y;
920 layer->inter_rect.x = rect_pos.x;
921 layer->inter_rect.y = mouse_pos.y;
925 snap_rect_move_if_enabled(layer, &layer->inter_rect,
926 SNAPPING_THRESHOLD / camera->scale);
929 case SDL_MOUSEBUTTONUP: {
930 layer->state = RECT_LAYER_IDLE;
932 float distance = vec_length(
933 vec_sub(rect_position(layer->inter_rect),
934 rect_position(rects[layer->selection])));
936 if (distance > 1e-6) {
939 create_rect_undo_update_context(
940 layer, (size_t) layer->selection));
943 (size_t) layer->selection,
951 static int rect_layer_event_id_rename(RectLayer *layer,
952 const SDL_Event *event,
953 const Camera *camera,
954 UndoHistory *undo_history)
958 trace_assert(camera);
959 trace_assert(layer->selection >= 0);
961 switch (event->type) {
963 switch (event->key.keysym.sym) {
967 create_rect_undo_update_context(
969 (size_t) layer->selection));
971 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
972 memset(id, 0, ENTITY_MAX_ID_SIZE);
973 memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
974 layer->state = RECT_LAYER_IDLE;
979 layer->state = RECT_LAYER_IDLE;
986 return edit_field_event(&layer->id_edit_field, event);
989 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
998 RectLayer *create_rect_layer(Memory *memory,
999 const char *id_name_prefix,
1002 trace_assert(memory);
1003 trace_assert(id_name_prefix);
1004 trace_assert(cursor);
1006 RectLayer *rect_layer = memory_alloc(memory, sizeof(RectLayer));
1008 rect_layer->ids = create_dynarray(memory, sizeof(char) * ENTITY_MAX_ID_SIZE);
1009 rect_layer->rects = create_dynarray(memory, sizeof(Rect));
1010 rect_layer->colors = create_dynarray(memory, sizeof(Color));
1011 rect_layer->actions = create_dynarray(memory, sizeof(Action));
1012 rect_layer->id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
1013 rect_layer->id_edit_field.font_color = COLOR_BLACK;
1014 rect_layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
1015 rect_layer->selection = -1;
1016 rect_layer->id_name_prefix = id_name_prefix;
1017 rect_layer->cursor = cursor;
1022 void rect_layer_load(RectLayer *layer, Memory *memory, String *input)
1024 trace_assert(layer);
1025 trace_assert(memory);
1026 trace_assert(input);
1028 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
1029 char id[ENTITY_MAX_ID_SIZE];
1030 for (int i = 0; i < n; ++i) {
1032 String line = trim(chop_by_delim(input, '\n'));
1033 String string_id = trim(chop_word(&line));
1034 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1035 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1036 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1037 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1038 Color color = hexs(trim(chop_word(&line)));
1040 memset(id, 0, ENTITY_MAX_ID_SIZE);
1044 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
1046 dynarray_push(&layer->rects, &rect);
1047 dynarray_push(&layer->colors, &color);
1048 dynarray_push(&layer->ids, id);
1051 .type = ACTION_NONE,
1055 String action_string = trim(chop_word(&line));
1056 if (action_string.count > 0) {
1057 action.type = (ActionType)atol(string_to_cstr(memory, action_string));
1058 switch (action.type) {
1059 case ACTION_NONE: break;
1060 case ACTION_TOGGLE_GOAL:
1061 case ACTION_HIDE_LABEL: {
1062 String label_id = trim(chop_word(&line));
1063 trace_assert(label_id.count > 0);
1064 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
1065 memcpy(action.entity_id,
1068 ENTITY_MAX_ID_SIZE - 1,
1072 case ACTION_N: break;
1076 dynarray_push(&layer->actions, &action);
1081 void render_tool_bar_button(const Camera *camera,
1082 Rect button_rect, const char *text,
1083 Color background_color,
1084 Color foreground_color)
1086 const Vec2f text_size = vec(5.0f, 5.0f);
1087 const Rect text_rect = sprite_font_boundary_box(
1088 vec(0.0f, 0.0f), text_size, text);
1089 camera_fill_rect_screen(
1093 camera_render_text_screen(
1099 button_rect.x + (button_rect.w - text_rect.w) * 0.5f,
1100 button_rect.y + (button_rect.h - text_rect.h) * 0.5f));
1104 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
1106 trace_assert(layer);
1107 trace_assert(camera);
1109 const size_t n = layer->rects.count;
1110 Rect *rects = (Rect *)layer->rects.data;
1111 Color *colors = (Color *)layer->colors.data;
1112 const char *ids = (const char *)layer->ids.data;
1115 for (size_t i = 0; i < n; ++i) {
1116 Rect rect = rects[i];
1117 Color color = colors[i];
1119 if (layer->selection == (int) i) {
1120 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1121 rect = layer->inter_rect;
1124 if (layer->state == RECT_LAYER_RECOLOR) {
1125 color = layer->inter_color;
1130 if (camera_fill_rect(
1135 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
1140 // Selection Overlay
1141 if (active && layer->selection >= 0) {
1142 Rect rect = rects[layer->selection];
1143 Color color = colors[layer->selection];
1145 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1146 rect = layer->inter_rect;
1149 if (layer->state == RECT_LAYER_RECOLOR) {
1150 color = layer->inter_color;
1153 const Rect overlay_rect =
1155 camera_rect(camera, rect),
1156 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1157 const Color overlay_color = color_invert(color);
1160 if (camera_draw_thicc_rect_screen(
1164 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1168 const Vec2f rect_id_pos = vec_sub(
1169 rect_position(rect),
1171 RECT_LAYER_ID_LABEL_SIZE,
1172 vec(0.0f, FONT_CHAR_HEIGHT)));
1175 if (layer->state == RECT_LAYER_ID_RENAME) {
1176 // ID renaming Edit Field
1177 if (edit_field_render_world(
1178 &layer->id_edit_field,
1185 if (camera_render_text(
1187 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1188 RECT_LAYER_ID_LABEL_SIZE,
1189 color_invert(color),
1197 const Color color = color_picker_rgba(&layer->color_picker);
1198 if (layer->state == RECT_LAYER_CREATE) {
1199 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1204 if (layer->state == RECT_LAYER_SUBTRACT) {
1205 if (camera_draw_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1210 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1216 render_tool_bar_button(
1217 camera, subtract_tool_button_rect(camera), "/",
1218 layer->subtract_enabled ? TOOL_BAR_BACKGROUND : TOOL_BAR_FOREGROUND,
1219 layer->subtract_enabled ? TOOL_BAR_FOREGROUND : TOOL_BAR_BACKGROUND);
1221 render_tool_bar_button(
1222 camera, snapping_tool_button_rect(camera), "S",
1223 layer->snapping_enabled ? TOOL_BAR_BACKGROUND : TOOL_BAR_FOREGROUND,
1224 layer->snapping_enabled ? TOOL_BAR_FOREGROUND : TOOL_BAR_BACKGROUND);
1231 int rect_layer_event_recolor(RectLayer *layer,
1232 const SDL_Event *event,
1233 const Camera *camera,
1234 UndoHistory *undo_history)
1236 trace_assert(layer);
1237 trace_assert(event);
1238 trace_assert(camera);
1239 trace_assert(undo_history);
1240 trace_assert(layer->selection >= 0);
1242 int color_changed = 0;
1243 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1247 if (color_changed) {
1248 layer->inter_color = color_picker_rgba(&layer->color_picker);
1250 if (!color_picker_drag(&layer->color_picker)) {
1253 create_rect_undo_update_context(
1255 (size_t)layer->selection));
1256 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1257 layer->state = RECT_LAYER_IDLE;
1264 int rect_layer_event(RectLayer *layer,
1265 const SDL_Event *event,
1266 const Camera *camera,
1267 UndoHistory *undo_history)
1269 trace_assert(layer);
1270 trace_assert(event);
1271 trace_assert(undo_history);
1273 switch (layer->state) {
1274 case RECT_LAYER_IDLE:
1275 return rect_layer_event_idle(layer, event, camera, undo_history);
1277 case RECT_LAYER_CREATE:
1278 return rect_layer_event_create(layer, event, camera, undo_history);
1280 case RECT_LAYER_RESIZE:
1281 return rect_layer_event_resize(layer, event, camera, undo_history);
1283 case RECT_LAYER_MOVE:
1284 return rect_layer_event_move(layer, event, camera, undo_history);
1286 case RECT_LAYER_ID_RENAME:
1287 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1289 case RECT_LAYER_RECOLOR:
1290 return rect_layer_event_recolor(layer, event, camera, undo_history);
1292 case RECT_LAYER_SUBTRACT:
1293 return rect_layer_event_subtract(layer, event, camera, undo_history);
1300 size_t rect_layer_count(const RectLayer *layer)
1302 return layer->rects.count;
1305 const Rect *rect_layer_rects(const RectLayer *layer)
1307 return (const Rect *)layer->rects.data;
1310 const Color *rect_layer_colors(const RectLayer *layer)
1312 return (const Color *)layer->colors.data;
1315 const char *rect_layer_ids(const RectLayer *layer)
1317 return (const char *)layer->ids.data;
1320 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1322 trace_assert(layer);
1323 trace_assert(filedump);
1325 size_t n = layer->ids.count;
1326 char *ids = (char *)layer->ids.data;
1327 Rect *rects = (Rect *)layer->rects.data;
1328 Color *colors = (Color *)layer->colors.data;
1329 Action *actions = (Action *)layer->actions.data;
1331 fprintf(filedump, "%zd\n", n);
1332 for (size_t i = 0; i < n; ++i) {
1333 fprintf(filedump, "%s %f %f %f %f ",
1334 ids + ENTITY_MAX_ID_SIZE * i,
1335 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1336 color_hex_to_stream(colors[i], filedump);
1338 switch (actions[i].type) {
1339 case ACTION_NONE: {} break;
1341 case ACTION_TOGGLE_GOAL:
1342 case ACTION_HIDE_LABEL: {
1343 fprintf(filedump, " %d %.*s",
1344 (int)actions[i].type,
1345 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1347 case ACTION_N: break;
1350 fprintf(filedump, "\n");
1356 const Action *rect_layer_actions(const RectLayer *layer)
1358 return (const Action *)layer->actions.data;