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/line_stream.h"
14 #include "color_picker.h"
15 #include "system/str.h"
16 #include "ui/edit_field.h"
17 #include "undo_history.h"
18 #include "game/level/action.h"
19 #include "action_picker.h"
22 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
23 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
24 #define CREATE_AREA_THRESHOLD 10.0
25 #define RECT_LAYER_GRID_ROWS 3
26 #define RECT_LAYER_GRID_COLUMNS 4
28 static int rect_clipboard = 0;
29 static Rect rect_clipboard_rect;
30 static Color rect_clipboard_color;
32 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
34 CURSOR_STYLE_RESIZE_VERT, // [1]
35 CURSOR_STYLE_RESIZE_HORIS, // [2]
36 CURSOR_STYLE_RESIZE_DIAG1, // [3]
37 CURSOR_STYLE_RESIZE_VERT, // [4]
39 CURSOR_STYLE_RESIZE_DIAG2, // [6]
41 CURSOR_STYLE_RESIZE_HORIS, // [8]
42 CURSOR_STYLE_RESIZE_DIAG2, // [9]
45 CURSOR_STYLE_RESIZE_DIAG1 // [12]
65 ColorPicker color_picker;
66 ActionPicker action_picker;
70 Vec2f move_anchor; // The mouse offset from the left-top
71 // corner of the rect during moving it
72 Edit_field *id_edit_field;
76 const char *id_name_prefix;
98 char id[ENTITY_MAX_ID_SIZE];
119 UndoElementContext element;
120 UndoSwapContext swap;
124 RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
127 trace_assert(index < layer->rects.count);
129 RectUndoContext undo_context;
130 undo_context.add.type = RECT_UNDO_ADD;
131 undo_context.add.layer = layer;
132 undo_context.add.index = index;
137 RectUndoContext create_rect_undo_element_context(RectLayer *layer)
140 size_t index = (size_t) layer->selection;
141 trace_assert(index < layer->rects.count);
143 RectUndoContext undo_context;
144 undo_context.element.layer = layer;
145 undo_context.element.index = index;
146 dynarray_copy_to(&layer->rects, &undo_context.element.rect, index);
147 dynarray_copy_to(&layer->colors, &undo_context.element.color, index);
148 dynarray_copy_to(&layer->ids, undo_context.element.id, index);
149 dynarray_copy_to(&layer->actions, &undo_context.element.action, index);
154 RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer)
156 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
157 undo_context.type = RECT_UNDO_UPDATE;
162 RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer)
164 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
165 undo_context.type = RECT_UNDO_DELETE;
170 RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
172 RectUndoContext undo_context;
173 undo_context.swap.type = RECT_UNDO_SWAP;
174 undo_context.swap.layer = rect_layer;
175 undo_context.swap.index1 = index1;
176 undo_context.swap.index2 = index2;
181 void rect_layer_undo(void *context, size_t context_size)
183 trace_assert(context);
184 trace_assert(sizeof(RectUndoContext) == context_size);
186 RectUndoContext *undo_context = context;
188 switch (undo_context->type) {
189 case RECT_UNDO_ADD: {
190 RectLayer *layer = undo_context->add.layer;
191 dynarray_delete_at(&layer->rects, undo_context->add.index);
192 dynarray_delete_at(&layer->colors, undo_context->add.index);
193 dynarray_delete_at(&layer->ids, undo_context->add.index);
194 dynarray_delete_at(&layer->actions, undo_context->add.index);
195 layer->selection = -1;
198 case RECT_UNDO_DELETE: {
199 RectLayer *layer = undo_context->element.layer;
200 dynarray_insert_before(&layer->rects, undo_context->element.index, &undo_context->element.rect);
201 dynarray_insert_before(&layer->colors, undo_context->element.index, &undo_context->element.color);
202 dynarray_insert_before(&layer->ids, undo_context->element.index, &undo_context->element.id);
203 dynarray_insert_before(&layer->actions, undo_context->element.index, &undo_context->element.action);
204 layer->selection = -1;
207 case RECT_UNDO_UPDATE: {
208 RectLayer *layer = undo_context->element.layer;
209 dynarray_replace_at(&layer->rects, undo_context->element.index, &undo_context->element.rect);
210 dynarray_replace_at(&layer->colors, undo_context->element.index, &undo_context->element.color);
211 dynarray_replace_at(&layer->ids, undo_context->element.index, &undo_context->element.id);
212 dynarray_replace_at(&layer->actions, undo_context->element.index, &undo_context->element.action);
215 case RECT_UNDO_SWAP: {
216 RectLayer *layer = undo_context->element.layer;
217 dynarray_swap(&layer->rects, undo_context->swap.index1, undo_context->swap.index2);
218 dynarray_swap(&layer->colors, undo_context->swap.index1, undo_context->swap.index2);
219 dynarray_swap(&layer->ids, undo_context->swap.index1, undo_context->swap.index2);
220 dynarray_swap(&layer->actions, undo_context->swap.index1, undo_context->swap.index2);
225 #define RECT_UNDO_PUSH(HISTORY, CONTEXT) \
227 RectUndoContext context = (CONTEXT); \
235 static int rect_layer_add_rect(RectLayer *layer,
238 UndoHistory *undo_history)
242 dynarray_push(&layer->rects, &rect);
243 dynarray_push(&layer->colors, &color);
245 char id[ENTITY_MAX_ID_SIZE];
246 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
247 layer->id_name_prefix,
248 layer->id_name_counter++);
249 dynarray_push(&layer->ids, id);
251 dynarray_push_empty(&layer->actions);
255 create_rect_undo_add_context(
257 layer->rects.count - 1));
262 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
266 int n = (int) layer->rects.count;
267 Rect *rects = (Rect*)layer->rects.data;
269 for (int i = n - 1; i >= 0; --i) {
270 if (rect_contains_point(rects[i], position)) {
278 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
279 UndoHistory *undo_history)
282 trace_assert(a < layer->rects.count);
283 trace_assert(b < layer->rects.count);
285 dynarray_swap(&layer->rects, a, b);
286 dynarray_swap(&layer->colors, a, b);
287 dynarray_swap(&layer->ids, a, b);
288 dynarray_swap(&layer->actions, a, b);
290 RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
293 static int rect_layer_delete_rect_at(RectLayer *layer,
295 UndoHistory *undo_history)
299 RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer));
301 dynarray_delete_at(&layer->rects, i);
302 dynarray_delete_at(&layer->colors, i);
303 dynarray_delete_at(&layer->ids, i);
304 dynarray_delete_at(&layer->actions, i);
309 static int calc_resize_mask(Vec2f point, Rect rect)
312 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
313 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
314 mask = mask | (1 << side);
320 static int rect_layer_event_idle(RectLayer *layer,
321 const SDL_Event *event,
322 const Camera *camera,
323 UndoHistory *undo_history)
327 trace_assert(camera);
329 int color_changed = 0;
330 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
335 if (layer->selection >= 0) {
336 dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
337 layer->state = RECT_LAYER_RECOLOR;
342 Rect *rects = (Rect*)layer->rects.data;
344 switch (event->type) {
345 case SDL_MOUSEBUTTONDOWN: {
346 switch (event->button.button) {
347 case SDL_BUTTON_LEFT: {
348 Vec2f position = camera_map_screen(
352 int rect_at_position =
353 rect_layer_rect_at(layer, position);
356 Color *colors = (Color*)layer->colors.data;
358 if (layer->selection >= 0 &&
359 layer->selection == rect_at_position &&
360 (layer->resize_mask = calc_resize_mask(
361 vec((float) event->button.x, (float)event->button.y),
362 camera_rect(camera, rects[layer->selection])))) {
363 layer->state = RECT_LAYER_RESIZE;
364 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
365 } else if (rect_at_position >= 0) {
366 layer->selection = rect_at_position;
367 layer->state = RECT_LAYER_MOVE;
368 layer->move_anchor = vec_sub(
371 rects[layer->selection].x,
372 rects[layer->selection].y));
373 layer->color_picker =
374 create_color_picker_from_rgba(colors[rect_at_position]);
375 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
377 layer->selection = rect_at_position;
379 if (layer->selection < 0) {
380 layer->state = RECT_LAYER_CREATE;
381 layer->create_begin = position;
382 layer->create_end = position;
389 case SDL_MOUSEMOTION: {
391 Vec2f position = camera_map_screen(
395 if (layer->selection >= 0 &&
396 layer->selection == rect_layer_rect_at(layer, position) &&
397 (resize_mask = calc_resize_mask(
398 vec((float) event->button.x, (float)event->button.y),
399 camera_rect(camera, rects[layer->selection])))) {
400 layer->cursor->style = resize_styles[resize_mask];
402 layer->cursor->style = CURSOR_STYLE_POINTER;
407 switch (event->key.keysym.sym) {
409 if ((event->key.keysym.mod & KMOD_SHIFT)
410 && (layer->selection >= 0)
411 && ((size_t)(layer->selection + 1) < layer->rects.count)) {
412 rect_layer_swap_elements(
414 (size_t) layer->selection,
415 (size_t) layer->selection + 1,
422 if ((event->key.keysym.mod & KMOD_SHIFT)
423 && (layer->selection > 0)
424 && ((size_t) layer->selection < layer->rects.count)) {
425 rect_layer_swap_elements(
427 (size_t) layer->selection,
428 (size_t) layer->selection - 1,
435 if (layer->selection >= 0) {
436 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
437 layer->selection = -1;
442 // TODO(#1171): there is no UI indication that we are in the snapping mode
443 layer->snapping_enabled = !layer->snapping_enabled;
447 if (layer->selection >= 0) {
448 const char *ids = (char*)layer->ids.data;
449 Color *colors = (Color*)layer->colors.data;
452 layer->id_edit_field,
453 RECT_LAYER_ID_LABEL_SIZE,
454 color_invert(colors[layer->selection]));
456 layer->state = RECT_LAYER_ID_RENAME;
458 layer->id_edit_field,
459 ids + layer->selection * ENTITY_MAX_ID_SIZE);
460 SDL_StartTextInput();
465 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
467 dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
468 dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
473 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
475 SDL_GetMouseState(&x, &y);
476 Vec2f position = camera_map_screen(camera, x, y);
480 rect(position.x, position.y,
481 rect_clipboard_rect.w, rect_clipboard_rect.h),
482 rect_clipboard_color,
493 static int rect_layer_event_create(RectLayer *layer,
494 const SDL_Event *event,
495 const Camera *camera,
496 UndoHistory *undo_history)
500 trace_assert(camera);
502 switch (event->type) {
503 case SDL_MOUSEBUTTONUP: {
504 switch (event->button.button) {
505 case SDL_BUTTON_LEFT: {
506 const Rect real_rect =
510 const float area = real_rect.w * real_rect.h;
512 if (area >= CREATE_AREA_THRESHOLD) {
516 color_picker_rgba(&layer->color_picker),
519 log_info("The area is too small %f. Such small box won't be created.\n", area);
521 layer->state = RECT_LAYER_IDLE;
526 case SDL_MOUSEMOTION: {
527 layer->create_end = camera_map_screen(
537 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
540 trace_assert(layer->selection >= 0);
543 if (!layer->snapping_enabled) return;
545 Rect *rects = (Rect*)layer->rects.data;
546 size_t rects_size = layer->rects.count;
548 for (size_t i = 0; i < rects_size; ++i) {
549 if (i == (size_t) layer->selection) continue;
551 const Rect b = rects[i];
553 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
554 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
557 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
558 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
563 static int rect_layer_event_resize(RectLayer *layer,
564 const SDL_Event *event,
565 const Camera *camera,
566 UndoHistory *undo_history)
570 trace_assert(camera);
571 trace_assert(layer->selection >= 0);
573 Rect *rects = (Rect*)layer->rects.data;
575 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
577 switch (event->type) {
578 case SDL_MOUSEMOTION: {
579 Vec2f position = camera_map_screen(
584 switch (layer->resize_mask) {
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(
595 rect_position2(rects[layer->selection]));
599 Rect a = rect(position.x,
600 rects[layer->selection].y,
601 rects[layer->selection].w,
602 rects[layer->selection].h);
604 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
606 layer->inter_rect = rect_from_points(
608 rect_position2(rects[layer->selection]));
611 case 3: { // TOP,LEFT
615 rects[layer->selection].w,
616 rects[layer->selection].h);
618 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
620 layer->inter_rect = rect_from_points(
622 rect_position2(rects[layer->selection]));
626 Rect a = rect(rects[layer->selection].x,
628 rects[layer->selection].w,
629 rects[layer->selection].h);
631 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
633 layer->inter_rect = rect_from_points(
634 rect_position(rects[layer->selection]),
635 vec(rects[layer->selection].x + rects[layer->selection].w,
639 case 6: { // BOTTOM,LEFT
643 rects[layer->selection].w,
644 -rects[layer->selection].h);
646 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
648 layer->inter_rect = rect_from_points(
649 vec(a.x, rects[layer->selection].y),
650 vec(rects[layer->selection].x + rects[layer->selection].w,
655 Rect a = rect(position.x,
656 rects[layer->selection].y,
657 rects[layer->selection].w,
658 rects[layer->selection].h);
660 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
662 layer->inter_rect = rect_from_points(
663 rect_position(rects[layer->selection]),
664 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
667 case 9: { // TOP,RIGHT
671 -rects[layer->selection].w,
672 rects[layer->selection].h);
674 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
676 layer->inter_rect = rect_from_points(
677 vec(rects[layer->selection].x, a.y),
679 rects[layer->selection].y + rects[layer->selection].h));
682 case 12: { // BOTTOM,RIGHT
686 -rects[layer->selection].w,
687 -rects[layer->selection].h);
689 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
691 layer->inter_rect = rect_from_points(
692 rect_position(rects[layer->selection]),
699 case SDL_MOUSEBUTTONUP: {
700 layer->state = RECT_LAYER_IDLE;
701 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
702 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
710 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
711 float snapping_threshold)
715 trace_assert(layer->selection >= 0);
717 if (!layer->snapping_enabled) return;
719 Rect *rects = (Rect*)layer->rects.data;
720 size_t rects_size = layer->rects.count;
722 for (size_t i = 0; i < rects_size; ++i) {
723 if (i == (size_t) layer->selection) continue;
725 const Rect b = rects[i];
727 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
728 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
731 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
732 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
737 static int rect_layer_event_move(RectLayer *layer,
738 const SDL_Event *event,
739 const Camera *camera,
740 UndoHistory *undo_history)
744 trace_assert(camera);
745 trace_assert(layer->selection >= 0);
747 Rect *rects = (Rect*)layer->rects.data;
749 switch (event->type) {
750 case SDL_MOUSEMOTION: {
751 const Uint8 *state = SDL_GetKeyboardState(NULL);
752 const Vec2f mouse_pos = vec_sub(
759 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
760 layer->inter_rect.x = mouse_pos.x;
761 layer->inter_rect.y = mouse_pos.y;
763 const Vec2f rect_pos = rect_position(rects[layer->selection]);
765 const float dx = fabsf(rect_pos.x - mouse_pos.x);
766 const float dy = fabsf(rect_pos.y - mouse_pos.y);
769 layer->inter_rect.x = mouse_pos.x;
770 layer->inter_rect.y = rect_pos.y;
772 layer->inter_rect.x = rect_pos.x;
773 layer->inter_rect.y = mouse_pos.y;
777 snap_rect_move_if_enabled(layer, &layer->inter_rect,
778 SNAPPING_THRESHOLD / camera->scale);
781 case SDL_MOUSEBUTTONUP: {
782 layer->state = RECT_LAYER_IDLE;
784 float distance = vec_length(
785 vec_sub(rect_position(layer->inter_rect),
786 rect_position(rects[layer->selection])));
788 if (distance > 1e-6) {
789 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
790 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
797 static int rect_layer_event_id_rename(RectLayer *layer,
798 const SDL_Event *event,
799 const Camera *camera,
800 UndoHistory *undo_history)
804 trace_assert(camera);
805 trace_assert(layer->selection >= 0);
807 switch (event->type) {
809 switch (event->key.keysym.sym) {
811 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
813 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
814 memset(id, 0, ENTITY_MAX_ID_SIZE);
815 memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
816 layer->state = RECT_LAYER_IDLE;
821 layer->state = RECT_LAYER_IDLE;
828 return edit_field_event(layer->id_edit_field, event);
831 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
840 RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
842 trace_assert(cursor);
844 Lt *lt = create_lt();
846 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
852 layer->ids = create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE);
853 layer->rects = create_dynarray(sizeof(Rect));
854 layer->colors = create_dynarray(sizeof(Color));
855 layer->actions = create_dynarray(sizeof(Action));
857 layer->id_edit_field = PUSH_LT(
860 RECT_LAYER_ID_LABEL_SIZE,
863 if (layer->id_edit_field == NULL) {
872 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
874 if (layer->grid == NULL) {
877 layer->grid->rows = RECT_LAYER_GRID_ROWS;
878 layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
879 grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
881 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
882 layer->selection = -1;
883 layer->id_name_prefix = id_name_prefix;
884 layer->cursor = cursor;
889 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream,
890 const char *id_name_prefix,
893 trace_assert(line_stream);
895 RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
900 const char *line = line_stream_next(line_stream);
902 RETURN_LT(layer->lt, NULL);
906 if (sscanf(line, "%zu", &count) < 0) {
907 RETURN_LT(layer->lt, NULL);
910 for (size_t i = 0; i < count; ++i) {
911 line = line_stream_next(line_stream);
913 RETURN_LT(layer->lt, NULL);
918 char id[ENTITY_MAX_ID_SIZE];
922 "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
927 log_fail("%s\n", strerror(errno));
928 RETURN_LT(layer->lt, NULL);
932 Color color = hexstr(hex);
933 dynarray_push(&layer->rects, &rect);
934 dynarray_push(&layer->ids, id);
935 dynarray_push(&layer->colors, &color);
942 if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
944 switch (action.type) {
945 case ACTION_NONE: break;
947 case ACTION_TOGGLE_GOAL:
948 case ACTION_HIDE_LABEL: {
949 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
950 log_fail("%s\n", strerror(errno));
951 RETURN_LT(layer->lt, NULL);
955 case ACTION_N: break;
959 dynarray_push(&layer->actions, &action);
965 RectLayer *chop_rect_layer(Memory *memory,
967 const char *id_name_prefix,
970 trace_assert(memory);
973 RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
976 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
977 char id[ENTITY_MAX_ID_SIZE];
978 for (int i = 0; i < n; ++i) {
980 String line = trim(chop_by_delim(input, '\n'));
981 String string_id = trim(chop_word(&line));
982 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
983 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
984 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
985 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
986 Color color = hexs(trim(chop_word(&line)));
988 memset(id, 0, ENTITY_MAX_ID_SIZE);
992 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
994 dynarray_push(&layer->rects, &rect);
995 dynarray_push(&layer->colors, &color);
996 dynarray_push(&layer->ids, id);
1003 String action_string = trim(chop_word(&line));
1004 if (action_string.count > 0) {
1005 action.type = atoi(string_to_cstr(memory, action_string));
1006 switch (action.type) {
1007 case ACTION_NONE: break;
1008 case ACTION_TOGGLE_GOAL:
1009 case ACTION_HIDE_LABEL: {
1010 String label_id = trim(chop_word(&line));
1011 trace_assert(label_id.count > 0);
1012 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
1013 memcpy(action.entity_id,
1016 ENTITY_MAX_ID_SIZE - 1,
1020 case ACTION_N: break;
1024 dynarray_push(&layer->actions, &action);
1030 void destroy_rect_layer(RectLayer *layer)
1032 trace_assert(layer);
1034 free(layer->ids.data);
1035 free(layer->rects.data);
1036 free(layer->colors.data);
1037 free(layer->actions.data);
1039 RETURN_LT0(layer->lt);
1042 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
1044 trace_assert(layer);
1045 trace_assert(camera);
1047 const size_t n = layer->rects.count;
1048 Rect *rects = (Rect *)layer->rects.data;
1049 Color *colors = (Color *)layer->colors.data;
1050 const char *ids = (const char *)layer->ids.data;
1053 for (size_t i = 0; i < n; ++i) {
1054 Rect rect = rects[i];
1055 Color color = colors[i];
1057 if (layer->selection == (int) i) {
1058 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1059 rect = layer->inter_rect;
1062 if (layer->state == RECT_LAYER_RECOLOR) {
1063 color = layer->inter_color;
1068 if (camera_fill_rect(
1073 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
1078 // Selection Overlay
1079 if (active && layer->selection >= 0) {
1080 Rect rect = rects[layer->selection];
1081 Color color = colors[layer->selection];
1083 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1084 rect = layer->inter_rect;
1087 if (layer->state == RECT_LAYER_RECOLOR) {
1088 color = layer->inter_color;
1091 const Rect overlay_rect =
1093 camera_rect(camera, rect),
1094 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1095 const Color overlay_color = color_invert(color);
1098 if (camera_draw_thicc_rect_screen(
1102 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1106 const Vec2f rect_id_pos = vec_sub(
1107 rect_position(rect),
1109 RECT_LAYER_ID_LABEL_SIZE,
1110 vec(0.0f, FONT_CHAR_HEIGHT)));
1113 if (layer->state == RECT_LAYER_ID_RENAME) {
1114 // ID renaming Edit Field
1115 if (edit_field_render_world(
1116 layer->id_edit_field,
1123 if (camera_render_text(
1125 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1126 RECT_LAYER_ID_LABEL_SIZE,
1127 color_invert(color),
1135 const Color color = color_picker_rgba(&layer->color_picker);
1136 if (layer->state == RECT_LAYER_CREATE) {
1137 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1142 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1150 int rect_layer_event_recolor(RectLayer *layer,
1151 const SDL_Event *event,
1152 const Camera *camera,
1153 UndoHistory *undo_history)
1155 trace_assert(layer);
1156 trace_assert(event);
1157 trace_assert(camera);
1158 trace_assert(undo_history);
1159 trace_assert(layer->selection >= 0);
1161 int color_changed = 0;
1162 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1166 if (color_changed) {
1167 layer->inter_color = color_picker_rgba(&layer->color_picker);
1169 if (!color_picker_drag(&layer->color_picker)) {
1170 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
1171 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1172 layer->state = RECT_LAYER_IDLE;
1179 int rect_layer_event(RectLayer *layer,
1180 const SDL_Event *event,
1181 const Camera *camera,
1182 UndoHistory *undo_history)
1184 trace_assert(layer);
1185 trace_assert(event);
1186 trace_assert(undo_history);
1188 switch (event->type) {
1189 case SDL_WINDOWEVENT: {
1190 switch (event->window.event) {
1191 case SDL_WINDOWEVENT_SIZE_CHANGED: {
1192 grid_relayout(layer->grid, rect(0.0f, 0.0f,
1193 (float) event->window.data1,
1194 (float) event->window.data2));
1200 switch (layer->state) {
1201 case RECT_LAYER_IDLE:
1202 return rect_layer_event_idle(layer, event, camera, undo_history);
1204 case RECT_LAYER_CREATE:
1205 return rect_layer_event_create(layer, event, camera, undo_history);
1207 case RECT_LAYER_RESIZE:
1208 return rect_layer_event_resize(layer, event, camera, undo_history);
1210 case RECT_LAYER_MOVE:
1211 return rect_layer_event_move(layer, event, camera, undo_history);
1213 case RECT_LAYER_ID_RENAME:
1214 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1216 case RECT_LAYER_RECOLOR:
1217 return rect_layer_event_recolor(layer, event, camera, undo_history);
1224 size_t rect_layer_count(const RectLayer *layer)
1226 return layer->rects.count;
1229 const Rect *rect_layer_rects(const RectLayer *layer)
1231 return (const Rect *)layer->rects.data;
1234 const Color *rect_layer_colors(const RectLayer *layer)
1236 return (const Color *)layer->colors.data;
1239 const char *rect_layer_ids(const RectLayer *layer)
1241 return (const char *)layer->ids.data;
1244 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1246 trace_assert(layer);
1247 trace_assert(filedump);
1249 size_t n = layer->ids.count;
1250 char *ids = (char *)layer->ids.data;
1251 Rect *rects = (Rect *)layer->rects.data;
1252 Color *colors = (Color *)layer->colors.data;
1253 Action *actions = (Action *)layer->actions.data;
1255 fprintf(filedump, "%zd\n", n);
1256 for (size_t i = 0; i < n; ++i) {
1257 fprintf(filedump, "%s %f %f %f %f ",
1258 ids + ENTITY_MAX_ID_SIZE * i,
1259 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1260 color_hex_to_stream(colors[i], filedump);
1262 switch (actions[i].type) {
1263 case ACTION_NONE: {} break;
1265 case ACTION_TOGGLE_GOAL:
1266 case ACTION_HIDE_LABEL: {
1267 fprintf(filedump, " %d %.*s",
1268 (int)actions[i].type,
1269 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1271 case ACTION_N: break;
1274 fprintf(filedump, "\n");
1280 const Action *rect_layer_actions(const RectLayer *layer)
1282 return (const Action *)layer->actions.data;