1 #include "game/camera.h"
3 #include "system/stacktrace.h"
4 #include "system/nth_alloc.h"
5 #include "system/log.h"
8 #include "rect_layer.h"
10 #include "system/line_stream.h"
11 #include "color_picker.h"
12 #include "system/str.h"
14 #define RECT_LAYER_ID_MAX_SIZE 36
15 #define RECT_LAYER_SELECTION_THICCNESS 5.0f
16 #define CREATE_AREA_THRESHOLD 10.0
25 /* TODO(#886): RectLayer does not allow to modify ids of Rects */
32 ColorPicker color_picker;
39 static int rect_layer_rect_at(RectLayer *layer, Vec position)
43 const size_t n = dynarray_count(layer->rects);
44 Rect *rects = dynarray_data(layer->rects);
46 for (size_t i = 0; i < n; ++i) {
47 if (rect_contains_point(rects[i], position)) {
55 static int rect_layer_delete_rect_at(RectLayer *layer, size_t i)
59 dynarray_delete_at(layer->rects, i);
60 dynarray_delete_at(layer->colors, i);
61 dynarray_delete_at(layer->ids, i);
66 static Rect rect_layer_resize_anchor(const RectLayer *layer, size_t i)
68 Rect *rects = dynarray_data(layer->rects);
69 return rect(rects[i].x + rects[i].w,
70 rects[i].y + rects[i].h,
71 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
72 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
75 static int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color)
79 if (dynarray_push(layer->rects, &rect) < 0) {
83 if (dynarray_push(layer->colors, &color) < 0) {
87 char id[RECT_LAYER_ID_MAX_SIZE];
88 for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
89 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
91 id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
93 if (dynarray_push(layer->ids, id)) {
101 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
110 RectLayer *create_rect_layer(void)
112 Lt *lt = create_lt();
114 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
120 layer->ids = PUSH_LT(
122 create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
124 if (layer->ids == NULL) {
128 layer->rects = PUSH_LT(
130 create_dynarray(sizeof(Rect)),
132 if (layer->rects == NULL) {
136 layer->colors = PUSH_LT(
138 create_dynarray(sizeof(Color)),
140 if (layer->colors == NULL) {
144 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
145 layer->selection = -1;
150 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
152 trace_assert(line_stream);
154 RectLayer *layer = create_rect_layer();
159 const char *line = line_stream_next(line_stream);
161 RETURN_LT(layer->lt, NULL);
165 if (sscanf(line, "%zu", &count) < 0) {
166 RETURN_LT(layer->lt, NULL);
169 for (size_t i = 0; i < count; ++i) {
170 line = line_stream_next(line_stream);
172 RETURN_LT(layer->lt, NULL);
177 char id[RECT_LAYER_ID_MAX_SIZE];
180 "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
185 RETURN_LT(layer->lt, NULL);
188 Color color = hexstr(hex);
190 dynarray_push(layer->rects, &rect);
191 dynarray_push(layer->ids, id);
192 dynarray_push(layer->colors, &color);
198 void destroy_rect_layer(RectLayer *layer)
201 RETURN_LT0(layer->lt);
204 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
207 trace_assert(camera);
209 const size_t n = dynarray_count(layer->rects);
210 Rect *rects = dynarray_data(layer->rects);
211 Color *colors = dynarray_data(layer->colors);
212 const char *ids = dynarray_data(layer->ids);
214 for (size_t i = 0; i < n; ++i) {
215 if (layer->selection == (int) i) {
217 const Color color = color_invert(colors[i]);
219 if (camera_fill_rect(
221 // TODO(#943): thiccness of RectLayer selection should be probably based on zoom
222 rect_scale(rects[i], RECT_LAYER_SELECTION_THICCNESS),
227 if (camera_fill_rect(
229 rect_layer_resize_anchor(layer, i),
236 if (camera_fill_rect(
241 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
245 if (camera_render_text(
247 ids + i * RECT_LAYER_ID_MAX_SIZE,
249 color_invert(colors[i]),
250 rect_position(rects[i])) < 0) {
255 const Color color = color_picker_rgba(&layer->color_picker);
256 if (layer->state == RECT_LAYER_CREATE) {
257 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
262 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
269 int rect_layer_event(RectLayer *layer, const SDL_Event *event, const Camera *camera)
275 if (color_picker_event(&layer->color_picker, event, &selected) < 0) {
283 const Color color = color_picker_rgba(&layer->color_picker);
285 switch (layer->state) {
286 case RECT_LAYER_CREATE: {
287 switch (event->type) {
288 case SDL_MOUSEBUTTONUP: {
289 switch (event->button.button) {
290 case SDL_BUTTON_LEFT: {
291 const Rect real_rect =
295 const float area = real_rect.w * real_rect.h;
297 if (area >= CREATE_AREA_THRESHOLD) {
298 rect_layer_add_rect(layer, real_rect, color);
300 log_info("The area is too small %f. Such small box won't be created.\n", area);
302 layer->state = RECT_LAYER_IDLE;
307 case SDL_MOUSEMOTION: {
308 layer->create_end = camera_map_screen(
316 case RECT_LAYER_RESIZE: {
317 switch (event->type) {
318 case SDL_MOUSEMOTION: {
319 Rect *rects = dynarray_data(layer->rects);
320 trace_assert(layer->selection >= 0);
321 rects[layer->selection] = rect_from_points(
322 vec(rects[layer->selection].x, rects[layer->selection].y),
328 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
329 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
332 case SDL_MOUSEBUTTONUP: {
333 layer->state = RECT_LAYER_IDLE;
338 case RECT_LAYER_IDLE: {
339 switch (event->type) {
340 case SDL_MOUSEBUTTONDOWN: {
341 switch (event->button.button) {
342 case SDL_BUTTON_LEFT: {
343 Point position = camera_map_screen(
347 int rect_at_position =
348 rect_layer_rect_at(layer, position);
350 if (rect_at_position >= 0) {
351 Rect *rects = dynarray_data(layer->rects);
352 layer->selection = rect_at_position;
353 layer->state = RECT_LAYER_MOVE;
358 rects[layer->selection].x,
359 rects[layer->selection].y));
360 } else if (layer->selection >= 0 && rect_contains_point(
361 rect_layer_resize_anchor(
363 (size_t)layer->selection),
365 layer->state = RECT_LAYER_RESIZE;
367 layer->selection = rect_at_position;
369 if (layer->selection < 0) {
370 layer->state = RECT_LAYER_CREATE;
371 layer->create_begin = position;
372 layer->create_end = position;
380 switch (event->key.keysym.sym) {
382 if (layer->selection >= 0) {
383 rect_layer_delete_rect_at(layer, (size_t) layer->selection);
384 layer->selection = -1;
392 case RECT_LAYER_MOVE: {
393 switch (event->type) {
394 case SDL_MOUSEMOTION: {
395 Point position = vec_sub(
402 Rect *rects = dynarray_data(layer->rects);
404 trace_assert(layer->selection >= 0);
406 rects[layer->selection].x = position.x;
407 rects[layer->selection].y = position.y;
410 case SDL_MOUSEBUTTONUP: {
411 layer->state = RECT_LAYER_IDLE;
420 size_t rect_layer_count(const RectLayer *layer)
422 return dynarray_count(layer->rects);
425 const Rect *rect_layer_rects(const RectLayer *layer)
427 return dynarray_data(layer->rects);
430 const Color *rect_layer_colors(const RectLayer *layer)
432 return dynarray_data(layer->colors);
435 const char *rect_layer_ids(const RectLayer *layer)
437 return dynarray_data(layer->ids);
440 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
443 trace_assert(filedump);
445 size_t n = dynarray_count(layer->ids);
446 char *ids = dynarray_data(layer->ids);
447 Rect *rects = dynarray_data(layer->rects);
448 Color *colors = dynarray_data(layer->colors);
450 fprintf(filedump, "%zd\n", n);
451 for (size_t i = 0; i < n; ++i) {
452 fprintf(filedump, "%s %f %f %f %f ",
453 ids + RECT_LAYER_ID_MAX_SIZE * i,
454 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
455 color_hex_to_stream(colors[i], filedump);
456 fprintf(filedump, "\n");