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 PROTO_AREA_THRESHOLD 10.0
18 // TODO(#942): RectLayer does not allow to move rectangle arround
26 /* TODO(#886): RectLayer does not allow to modify ids of Rects */
33 ColorPicker color_picker;
40 static int rect_layer_rect_at(RectLayer *layer, Vec position)
44 const size_t n = dynarray_count(layer->rects);
45 Rect *rects = dynarray_data(layer->rects);
47 for (size_t i = 0; i < n; ++i) {
48 if (rect_contains_point(rects[i], position)) {
56 static int rect_layer_delete_rect_at(RectLayer *layer, size_t i)
60 dynarray_delete_at(layer->rects, i);
61 dynarray_delete_at(layer->colors, i);
62 dynarray_delete_at(layer->ids, i);
67 static Rect rect_layer_resize_anchor(const RectLayer *layer, size_t i)
69 Rect *rects = dynarray_data(layer->rects);
70 return rect(rects[i].x + rects[i].w,
71 rects[i].y + rects[i].h,
72 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
73 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
76 static int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color)
80 if (dynarray_push(layer->rects, &rect) < 0) {
84 if (dynarray_push(layer->colors, &color) < 0) {
88 char id[RECT_LAYER_ID_MAX_SIZE];
89 for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
90 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
92 id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
94 if (dynarray_push(layer->ids, id)) {
102 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
111 RectLayer *create_rect_layer(void)
113 Lt *lt = create_lt();
115 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
121 layer->ids = PUSH_LT(
123 create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
125 if (layer->ids == NULL) {
129 layer->rects = PUSH_LT(
131 create_dynarray(sizeof(Rect)),
133 if (layer->rects == NULL) {
137 layer->colors = PUSH_LT(
139 create_dynarray(sizeof(Color)),
141 if (layer->colors == NULL) {
145 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
146 layer->selection = -1;
151 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
153 trace_assert(line_stream);
155 RectLayer *layer = create_rect_layer();
160 const char *line = line_stream_next(line_stream);
162 RETURN_LT(layer->lt, NULL);
166 if (sscanf(line, "%zu", &count) < 0) {
167 RETURN_LT(layer->lt, NULL);
170 for (size_t i = 0; i < count; ++i) {
171 line = line_stream_next(line_stream);
173 RETURN_LT(layer->lt, NULL);
178 char id[RECT_LAYER_ID_MAX_SIZE];
181 "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
186 RETURN_LT(layer->lt, NULL);
189 Color color = hexstr(hex);
191 dynarray_push(layer->rects, &rect);
192 dynarray_push(layer->ids, id);
193 dynarray_push(layer->colors, &color);
199 void destroy_rect_layer(RectLayer *layer)
202 RETURN_LT0(layer->lt);
205 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
208 trace_assert(camera);
210 const size_t n = dynarray_count(layer->rects);
211 Rect *rects = dynarray_data(layer->rects);
212 Color *colors = dynarray_data(layer->colors);
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) {
247 const Color color = color_picker_rgba(&layer->color_picker);
248 if (layer->state == RECT_LAYER_PROTO) {
249 if (camera_fill_rect(camera, rect_from_points(layer->proto_begin, layer->proto_end), color) < 0) {
254 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
261 int rect_layer_event(RectLayer *layer, const SDL_Event *event, const Camera *camera)
267 if (color_picker_event(&layer->color_picker, event, &selected) < 0) {
275 const Color color = color_picker_rgba(&layer->color_picker);
277 switch (layer->state) {
278 case RECT_LAYER_PROTO: {
279 switch (event->type) {
280 case SDL_MOUSEBUTTONUP: {
281 switch (event->button.button) {
282 case SDL_BUTTON_LEFT: {
283 const Rect real_rect =
287 const float area = real_rect.w * real_rect.h;
289 if (area >= PROTO_AREA_THRESHOLD) {
290 rect_layer_add_rect(layer, real_rect, color);
292 log_info("The area is too small %f. Such small box won't be created.\n", area);
294 layer->state = RECT_LAYER_IDLE;
299 case SDL_MOUSEMOTION: {
300 layer->proto_end = camera_map_screen(
308 case RECT_LAYER_RESIZE: {
309 switch (event->type) {
310 case SDL_MOUSEMOTION: {
311 Rect *rects = dynarray_data(layer->rects);
312 trace_assert(layer->selection >= 0);
313 rects[layer->selection] = rect_from_points(
314 vec(rects[layer->selection].x, rects[layer->selection].y),
320 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
321 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
324 case SDL_MOUSEBUTTONUP: {
325 layer->state = RECT_LAYER_IDLE;
330 case RECT_LAYER_IDLE: {
331 switch (event->type) {
332 case SDL_MOUSEBUTTONDOWN: {
333 switch (event->button.button) {
334 case SDL_BUTTON_LEFT: {
335 Point position = camera_map_screen(
339 int rect_at_position =
340 rect_layer_rect_at(layer, position);
342 if (rect_at_position >= 0) {
343 Rect *rects = dynarray_data(layer->rects);
344 layer->selection = rect_at_position;
345 layer->state = RECT_LAYER_MOVE;
350 rects[layer->selection].x,
351 rects[layer->selection].y));
352 } else if (layer->selection >= 0 && rect_contains_point(
353 rect_layer_resize_anchor(
355 (size_t)layer->selection),
357 layer->state = RECT_LAYER_RESIZE;
359 layer->selection = rect_at_position;
361 if (layer->selection < 0) {
362 layer->state = RECT_LAYER_PROTO;
363 layer->proto_begin = position;
364 layer->proto_end = position;
372 switch (event->key.keysym.sym) {
374 if (layer->selection >= 0) {
375 rect_layer_delete_rect_at(layer, (size_t) layer->selection);
376 layer->selection = -1;
384 case RECT_LAYER_MOVE: {
385 switch (event->type) {
386 case SDL_MOUSEMOTION: {
387 Point position = vec_sub(
394 Rect *rects = dynarray_data(layer->rects);
396 trace_assert(layer->selection >= 0);
398 rects[layer->selection].x = position.x;
399 rects[layer->selection].y = position.y;
402 case SDL_MOUSEBUTTONUP: {
403 layer->state = RECT_LAYER_IDLE;
412 size_t rect_layer_count(const RectLayer *layer)
414 return dynarray_count(layer->rects);
417 const Rect *rect_layer_rects(const RectLayer *layer)
419 return dynarray_data(layer->rects);
422 const Color *rect_layer_colors(const RectLayer *layer)
424 return dynarray_data(layer->colors);
427 const char *rect_layer_ids(const RectLayer *layer)
429 return dynarray_data(layer->ids);
432 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
435 trace_assert(filedump);
437 size_t n = dynarray_count(layer->ids);
438 char *ids = dynarray_data(layer->ids);
439 Rect *rects = dynarray_data(layer->rects);
440 Color *colors = dynarray_data(layer->colors);
442 fprintf(filedump, "%zd\n", n);
443 for (size_t i = 0; i < n; ++i) {
444 fprintf(filedump, "%s %f %f %f %f ",
445 ids + RECT_LAYER_ID_MAX_SIZE * i,
446 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
447 color_hex_to_stream(colors[i], filedump);
448 fprintf(filedump, "\n");