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 assert(geometry->count < GEOMETRY_CAPACITY);
510 if ((rect.w * rect.h) > 1e-6f) {
511 size_t i = (geometry->first + geometry->count) % GEOMETRY_CAPACITY;
512 geometry->rects[i] = rect;
513 geometry->colors[i] = color;
519 void subtract_rect_from_rect(Rect a, Color color_a, Rect c, Geometry *result)
523 Rect b = rects_overlap_area(a, c);
525 if (b.w * b.h < 1e-6) {
526 push_geometry(result, a, color_a);
530 push_geometry(result, (Rect) {a.x, a.y, a.w, b.y - a.y}, color_a);
531 push_geometry(result, (Rect) {a.x, b.y, b.x - a.x, b.h}, color_a);
532 push_geometry(result, (Rect) {
535 a.w - (b.x - a.x) - b.w,
538 push_geometry(result, (Rect) {
542 a.h - (b.y - a.y) - b.h
547 void subtract_rect_from_geometry(Geometry *result, Rect b)
551 size_t count = result->count;
552 size_t first = result->first;
554 for (size_t i = 0; i < count; ++i) {
555 result->first = (result->first + 1) % GEOMETRY_CAPACITY;
557 subtract_rect_from_rect(
558 result->rects[(i + first) % GEOMETRY_CAPACITY],
559 result->colors[(i + first) % GEOMETRY_CAPACITY],
565 static int rect_layer_event_subtract(RectLayer *layer,
566 const SDL_Event *event,
567 const Camera *camera,
568 UndoHistory *undo_history)
572 trace_assert(camera);
573 trace_assert(undo_history);
575 Rect *rects = layer->rects.data;
576 Color *colors = layer->colors.data;
578 switch (event->type) {
579 case SDL_MOUSEBUTTONUP: {
580 switch (event->button.button) {
581 case SDL_BUTTON_LEFT: {
582 const Rect real_rect =
586 const float area = real_rect.w * real_rect.h;
588 Geometry geometry = {0};
590 if (area >= CREATE_AREA_THRESHOLD) {
591 for (size_t i = 0; i < layer->rects.count;) {
592 Rect overlap_area = rects_overlap_area(
595 if (overlap_area.w * overlap_area.h > 1e-6) {
596 push_geometry(&geometry, rects[i], colors[i]);
597 rect_layer_delete_rect_at_index(layer, i, undo_history);
603 subtract_rect_from_geometry(&geometry, real_rect);
605 for (size_t i = 0; i < geometry.count; ++i) {
606 size_t j = (i + geometry.first) % GEOMETRY_CAPACITY;
614 log_info("The area is too small %f. Such small box won't be cut.\n", area);
616 layer->state = RECT_LAYER_IDLE;
621 case SDL_MOUSEMOTION: {
622 layer->create_end = camera_map_screen(
632 static int rect_layer_event_create(RectLayer *layer,
633 const SDL_Event *event,
634 const Camera *camera,
635 UndoHistory *undo_history)
639 trace_assert(camera);
641 switch (event->type) {
642 case SDL_MOUSEBUTTONUP: {
643 switch (event->button.button) {
644 case SDL_BUTTON_LEFT: {
645 const Rect real_rect =
649 const float area = real_rect.w * real_rect.h;
651 if (area >= CREATE_AREA_THRESHOLD) {
655 color_picker_rgba(&layer->color_picker),
658 log_info("The area is too small %f. Such small box won't be created.\n", area);
660 layer->state = RECT_LAYER_IDLE;
665 case SDL_MOUSEMOTION: {
666 layer->create_end = camera_map_screen(
676 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
679 trace_assert(layer->selection >= 0);
682 if (!layer->snapping_enabled) return;
684 Rect *rects = (Rect*)layer->rects.data;
685 size_t rects_size = layer->rects.count;
687 for (size_t i = 0; i < rects_size; ++i) {
688 if (i == (size_t) layer->selection) continue;
690 const Rect b = rects[i];
692 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
693 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
696 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
697 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
702 static int rect_layer_event_resize(RectLayer *layer,
703 const SDL_Event *event,
704 const Camera *camera,
705 UndoHistory *undo_history)
709 trace_assert(camera);
710 trace_assert(layer->selection >= 0);
712 Rect *rects = (Rect*)layer->rects.data;
714 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
716 switch (event->type) {
717 case SDL_MOUSEMOTION: {
718 Vec2f position = camera_map_screen(
723 switch (layer->resize_mask) {
725 Rect a = rect(rects[layer->selection].x,
727 rects[layer->selection].w,
728 rects[layer->selection].h);
730 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
732 layer->inter_rect = rect_from_points(
734 rect_position2(rects[layer->selection]));
738 Rect a = rect(position.x,
739 rects[layer->selection].y,
740 rects[layer->selection].w,
741 rects[layer->selection].h);
743 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
745 layer->inter_rect = rect_from_points(
747 rect_position2(rects[layer->selection]));
750 case 3: { // TOP,LEFT
754 rects[layer->selection].w,
755 rects[layer->selection].h);
757 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
759 layer->inter_rect = rect_from_points(
761 rect_position2(rects[layer->selection]));
765 Rect a = rect(rects[layer->selection].x,
767 rects[layer->selection].w,
768 rects[layer->selection].h);
770 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
772 layer->inter_rect = rect_from_points(
773 rect_position(rects[layer->selection]),
774 vec(rects[layer->selection].x + rects[layer->selection].w,
778 case 6: { // BOTTOM,LEFT
782 rects[layer->selection].w,
783 -rects[layer->selection].h);
785 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
787 layer->inter_rect = rect_from_points(
788 vec(a.x, rects[layer->selection].y),
789 vec(rects[layer->selection].x + rects[layer->selection].w,
794 Rect a = rect(position.x,
795 rects[layer->selection].y,
796 rects[layer->selection].w,
797 rects[layer->selection].h);
799 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
801 layer->inter_rect = rect_from_points(
802 rect_position(rects[layer->selection]),
803 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
806 case 9: { // TOP,RIGHT
810 -rects[layer->selection].w,
811 rects[layer->selection].h);
813 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
815 layer->inter_rect = rect_from_points(
816 vec(rects[layer->selection].x, a.y),
818 rects[layer->selection].y + rects[layer->selection].h));
821 case 12: { // BOTTOM,RIGHT
825 -rects[layer->selection].w,
826 -rects[layer->selection].h);
828 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
830 layer->inter_rect = rect_from_points(
831 rect_position(rects[layer->selection]),
838 case SDL_MOUSEBUTTONUP: {
839 layer->state = RECT_LAYER_IDLE;
842 create_rect_undo_update_context(
844 (size_t) layer->selection));
845 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
853 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
854 float snapping_threshold)
858 trace_assert(layer->selection >= 0);
860 if (!layer->snapping_enabled) return;
862 Rect *rects = (Rect*)layer->rects.data;
863 size_t rects_size = layer->rects.count;
865 for (size_t i = 0; i < rects_size; ++i) {
866 if (i == (size_t) layer->selection) continue;
868 const Rect b = rects[i];
870 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
871 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
874 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
875 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
880 static int rect_layer_event_move(RectLayer *layer,
881 const SDL_Event *event,
882 const Camera *camera,
883 UndoHistory *undo_history)
887 trace_assert(camera);
888 trace_assert(layer->selection >= 0);
890 Rect *rects = (Rect*)layer->rects.data;
892 switch (event->type) {
893 case SDL_MOUSEMOTION: {
894 const Uint8 *state = SDL_GetKeyboardState(NULL);
895 const Vec2f mouse_pos = vec_sub(
902 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
903 layer->inter_rect.x = mouse_pos.x;
904 layer->inter_rect.y = mouse_pos.y;
906 const Vec2f rect_pos = rect_position(rects[layer->selection]);
908 const float dx = fabsf(rect_pos.x - mouse_pos.x);
909 const float dy = fabsf(rect_pos.y - mouse_pos.y);
912 layer->inter_rect.x = mouse_pos.x;
913 layer->inter_rect.y = rect_pos.y;
915 layer->inter_rect.x = rect_pos.x;
916 layer->inter_rect.y = mouse_pos.y;
920 snap_rect_move_if_enabled(layer, &layer->inter_rect,
921 SNAPPING_THRESHOLD / camera->scale);
924 case SDL_MOUSEBUTTONUP: {
925 layer->state = RECT_LAYER_IDLE;
927 float distance = vec_length(
928 vec_sub(rect_position(layer->inter_rect),
929 rect_position(rects[layer->selection])));
931 if (distance > 1e-6) {
934 create_rect_undo_update_context(
935 layer, (size_t) layer->selection));
938 (size_t) layer->selection,
946 static int rect_layer_event_id_rename(RectLayer *layer,
947 const SDL_Event *event,
948 const Camera *camera,
949 UndoHistory *undo_history)
953 trace_assert(camera);
954 trace_assert(layer->selection >= 0);
956 switch (event->type) {
958 switch (event->key.keysym.sym) {
962 create_rect_undo_update_context(
964 (size_t) layer->selection));
966 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
967 memset(id, 0, ENTITY_MAX_ID_SIZE);
968 memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
969 layer->state = RECT_LAYER_IDLE;
974 layer->state = RECT_LAYER_IDLE;
981 return edit_field_event(&layer->id_edit_field, event);
984 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
993 RectLayer *create_rect_layer(Memory *memory,
994 const char *id_name_prefix,
997 trace_assert(memory);
998 trace_assert(id_name_prefix);
999 trace_assert(cursor);
1001 RectLayer *rect_layer = memory_alloc(memory, sizeof(RectLayer));
1003 rect_layer->ids = create_dynarray(memory, sizeof(char) * ENTITY_MAX_ID_SIZE);
1004 rect_layer->rects = create_dynarray(memory, sizeof(Rect));
1005 rect_layer->colors = create_dynarray(memory, sizeof(Color));
1006 rect_layer->actions = create_dynarray(memory, sizeof(Action));
1007 rect_layer->id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
1008 rect_layer->id_edit_field.font_color = COLOR_BLACK;
1009 rect_layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
1010 rect_layer->selection = -1;
1011 rect_layer->id_name_prefix = id_name_prefix;
1012 rect_layer->cursor = cursor;
1017 void rect_layer_load(RectLayer *layer, Memory *memory, String *input)
1019 trace_assert(layer);
1020 trace_assert(memory);
1021 trace_assert(input);
1023 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
1024 char id[ENTITY_MAX_ID_SIZE];
1025 for (int i = 0; i < n; ++i) {
1027 String line = trim(chop_by_delim(input, '\n'));
1028 String string_id = trim(chop_word(&line));
1029 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1030 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1031 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1032 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
1033 Color color = hexs(trim(chop_word(&line)));
1035 memset(id, 0, ENTITY_MAX_ID_SIZE);
1039 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
1041 dynarray_push(&layer->rects, &rect);
1042 dynarray_push(&layer->colors, &color);
1043 dynarray_push(&layer->ids, id);
1046 .type = ACTION_NONE,
1050 String action_string = trim(chop_word(&line));
1051 if (action_string.count > 0) {
1052 action.type = (ActionType)atol(string_to_cstr(memory, action_string));
1053 switch (action.type) {
1054 case ACTION_NONE: break;
1055 case ACTION_TOGGLE_GOAL:
1056 case ACTION_HIDE_LABEL: {
1057 String label_id = trim(chop_word(&line));
1058 trace_assert(label_id.count > 0);
1059 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
1060 memcpy(action.entity_id,
1063 ENTITY_MAX_ID_SIZE - 1,
1067 case ACTION_N: break;
1071 dynarray_push(&layer->actions, &action);
1075 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
1077 trace_assert(layer);
1078 trace_assert(camera);
1080 const size_t n = layer->rects.count;
1081 Rect *rects = (Rect *)layer->rects.data;
1082 Color *colors = (Color *)layer->colors.data;
1083 const char *ids = (const char *)layer->ids.data;
1086 for (size_t i = 0; i < n; ++i) {
1087 Rect rect = rects[i];
1088 Color color = colors[i];
1090 if (layer->selection == (int) i) {
1091 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1092 rect = layer->inter_rect;
1095 if (layer->state == RECT_LAYER_RECOLOR) {
1096 color = layer->inter_color;
1101 if (camera_fill_rect(
1106 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
1111 // Selection Overlay
1112 if (active && layer->selection >= 0) {
1113 Rect rect = rects[layer->selection];
1114 Color color = colors[layer->selection];
1116 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1117 rect = layer->inter_rect;
1120 if (layer->state == RECT_LAYER_RECOLOR) {
1121 color = layer->inter_color;
1124 const Rect overlay_rect =
1126 camera_rect(camera, rect),
1127 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1128 const Color overlay_color = color_invert(color);
1131 if (camera_draw_thicc_rect_screen(
1135 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1139 const Vec2f rect_id_pos = vec_sub(
1140 rect_position(rect),
1142 RECT_LAYER_ID_LABEL_SIZE,
1143 vec(0.0f, FONT_CHAR_HEIGHT)));
1146 if (layer->state == RECT_LAYER_ID_RENAME) {
1147 // ID renaming Edit Field
1148 if (edit_field_render_world(
1149 &layer->id_edit_field,
1156 if (camera_render_text(
1158 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1159 RECT_LAYER_ID_LABEL_SIZE,
1160 color_invert(color),
1168 const Color color = color_picker_rgba(&layer->color_picker);
1169 if (layer->state == RECT_LAYER_CREATE) {
1170 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1175 if (layer->state == RECT_LAYER_SUBTRACT) {
1176 if (camera_draw_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1181 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1187 // TODO: subtract and snapping tools don't have any icons
1188 camera_fill_rect_screen(
1190 subtract_tool_button_rect(camera),
1191 layer->subtract_enabled ? COLOR_RED : rgba(0.2f, 0.2f, 0.2f, 1.0f));
1192 camera_fill_rect_screen(
1194 snapping_tool_button_rect(camera),
1195 layer->snapping_enabled ? COLOR_RED : rgba(0.2f, 0.2f, 0.2f, 1.0f));
1202 int rect_layer_event_recolor(RectLayer *layer,
1203 const SDL_Event *event,
1204 const Camera *camera,
1205 UndoHistory *undo_history)
1207 trace_assert(layer);
1208 trace_assert(event);
1209 trace_assert(camera);
1210 trace_assert(undo_history);
1211 trace_assert(layer->selection >= 0);
1213 int color_changed = 0;
1214 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1218 if (color_changed) {
1219 layer->inter_color = color_picker_rgba(&layer->color_picker);
1221 if (!color_picker_drag(&layer->color_picker)) {
1224 create_rect_undo_update_context(
1226 (size_t)layer->selection));
1227 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1228 layer->state = RECT_LAYER_IDLE;
1235 int rect_layer_event(RectLayer *layer,
1236 const SDL_Event *event,
1237 const Camera *camera,
1238 UndoHistory *undo_history)
1240 trace_assert(layer);
1241 trace_assert(event);
1242 trace_assert(undo_history);
1244 switch (layer->state) {
1245 case RECT_LAYER_IDLE:
1246 return rect_layer_event_idle(layer, event, camera, undo_history);
1248 case RECT_LAYER_CREATE:
1249 return rect_layer_event_create(layer, event, camera, undo_history);
1251 case RECT_LAYER_RESIZE:
1252 return rect_layer_event_resize(layer, event, camera, undo_history);
1254 case RECT_LAYER_MOVE:
1255 return rect_layer_event_move(layer, event, camera, undo_history);
1257 case RECT_LAYER_ID_RENAME:
1258 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1260 case RECT_LAYER_RECOLOR:
1261 return rect_layer_event_recolor(layer, event, camera, undo_history);
1263 case RECT_LAYER_SUBTRACT:
1264 return rect_layer_event_subtract(layer, event, camera, undo_history);
1271 size_t rect_layer_count(const RectLayer *layer)
1273 return layer->rects.count;
1276 const Rect *rect_layer_rects(const RectLayer *layer)
1278 return (const Rect *)layer->rects.data;
1281 const Color *rect_layer_colors(const RectLayer *layer)
1283 return (const Color *)layer->colors.data;
1286 const char *rect_layer_ids(const RectLayer *layer)
1288 return (const char *)layer->ids.data;
1291 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1293 trace_assert(layer);
1294 trace_assert(filedump);
1296 size_t n = layer->ids.count;
1297 char *ids = (char *)layer->ids.data;
1298 Rect *rects = (Rect *)layer->rects.data;
1299 Color *colors = (Color *)layer->colors.data;
1300 Action *actions = (Action *)layer->actions.data;
1302 fprintf(filedump, "%zd\n", n);
1303 for (size_t i = 0; i < n; ++i) {
1304 fprintf(filedump, "%s %f %f %f %f ",
1305 ids + ENTITY_MAX_ID_SIZE * i,
1306 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1307 color_hex_to_stream(colors[i], filedump);
1309 switch (actions[i].type) {
1310 case ACTION_NONE: {} break;
1312 case ACTION_TOGGLE_GOAL:
1313 case ACTION_HIDE_LABEL: {
1314 fprintf(filedump, " %d %.*s",
1315 (int)actions[i].type,
1316 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1318 case ACTION_N: break;
1321 fprintf(filedump, "\n");
1327 const Action *rect_layer_actions(const RectLayer *layer)
1329 return (const Action *)layer->actions.data;