#include #include #include #include #include #include #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-result" #define RAYGUI_IMPLEMENTATION #include #pragma clang diagnostic pop #include "a2.h" #include "bounding_box.h" #include "frame.h" #include "mapping.h" #include "mask.h" #include "pack.h" #include "png.h" namespace fs = std::filesystem; constexpr int resize_handle = 20; Rectangle rect_from_bbox(const freling::BoundingBox& box, const Vector2& offset) { const Rectangle rect = {offset.x + box.x, offset.y + box.y, static_cast(box.width), static_cast(box.height)}; return rect; } void draw_resizable(const freling::BoundingBox& box, const Vector2& offset, const Color& color) { const Rectangle rect = rect_from_bbox(box, offset); DrawRectangleRec(rect, ColorAlpha(color, 0.3)); DrawRectangleLinesEx(rect, 2, color); DrawTriangle( (Vector2){rect.x + rect.width - resize_handle, rect.y + rect.height}, (Vector2){rect.x + rect.width, rect.y + rect.height}, (Vector2){rect.x + rect.width, rect.y + rect.height - resize_handle}, color); } void draw_region(const freling::BoundingBox& box, const Vector2& offset, const Color& color) { const Rectangle rect = rect_from_bbox(box, offset); DrawRectangleRec(rect, ColorAlpha(color, 0.8)); } Image image_from_frame(const freling::Frame& frame) { Image image = {0}; const int format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; const unsigned int size = GetPixelDataSize(frame.width, frame.height, format); image.data = RL_MALLOC(size); memcpy(image.data, frame.data, size); image.width = frame.width; image.height = frame.height; image.mipmaps = 1; image.format = format; return image; } int main(int argc, const char* argv[]) { if (argc < 2 or (argc - 2) % 4 != 0) { std::cerr << "Usage: " << argv[0] << " path/to/image" << " [x y width height ...]\n" << "x/y/w/h points must be grouped by 4 to define bounding boxes\n"; return 1; } const fs::path input(argv[1]); if (!fs::exists(input)) { std::cerr << "Input file " << input << " does not exist.\n"; return 1; } const std::optional in_frame = freling::load_png(input); if (!in_frame) { std::cerr << "Cannot load file " << input << "\n"; return 1; } std::vector bboxes; bboxes.reserve((argc - 2) / 4); int i = 2; while (i < argc) { const int32_t x = atoi(argv[i]); const int32_t y = atoi(argv[i + 1]); const uint32_t w = atoi(argv[i + 2]); const uint32_t h = atoi(argv[i + 3]); bboxes.push_back(freling::BoundingBox({x, y, w, h})); i += 4; } const std::vector bbox_colors = {RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE}; std::vector packed_bboxes; std::vector a2_bboxes; std::vector a2_bboxes_origin; freling::Mask in_mask(in_frame->width, in_frame->height); Vector2 win_size = {800, 450}; const int padding = 5; const Vector2 in_offset = {padding, 40}; InitWindow(win_size.x, win_size.y, "netatmo - rect packing"); SetTargetFPS(60); Image in_img = image_from_frame(*in_frame); Texture2D in_texture = LoadTextureFromImage(in_img); Image pack_img; Texture2D pack_texture; bool packed = false; bool a2_processed = false; win_size.x = std::max(win_size.x, in_offset.x + in_texture.width + padding); win_size.y = std::max(win_size.y, in_offset.y + in_texture.height + padding); SetWindowSize(win_size.x, win_size.y); const Vector2 b_size = {150, 30}; Vector2 mouse_pos = {0}; Vector2 mouse_delta = {0}; constexpr int idx_unset = -1; int b_moving_idx = idx_unset; int b_resize_idx = idx_unset; int b_delete_idx = idx_unset; while (!WindowShouldClose()) { // // Update // mouse_pos = GetMousePosition(); mouse_delta = GetMouseDelta(); for (int b = 0, size = bboxes.size(); b < size; ++b) { const auto& box = bboxes[b]; const Rectangle rect = rect_from_bbox(box, in_offset); if (not CheckCollisionPointRec(mouse_pos, rect)) { continue; } if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { const Rectangle handle_area = { rect.x + rect.width - resize_handle, rect.y + rect.height - resize_handle, resize_handle, resize_handle}; if (CheckCollisionPointRec(mouse_pos, handle_area)) { b_resize_idx = b; } else { b_moving_idx = b; } } if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) { b_delete_idx = b; } } if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { b_moving_idx = idx_unset; b_resize_idx = idx_unset; } if (b_moving_idx != idx_unset) { auto& box = bboxes[b_moving_idx]; box.x += mouse_delta.x; box.y += mouse_delta.y; packed = false; } if (b_resize_idx != idx_unset) { auto& box = bboxes[b_resize_idx]; box.width = std::max(static_cast(box.width + mouse_delta.x), resize_handle); box.height = std::max(static_cast(box.height + mouse_delta.y), resize_handle); packed = false; } if (b_delete_idx != idx_unset) { bboxes.erase(bboxes.begin() + b_delete_idx); b_delete_idx = idx_unset; } a2_processed = a2_processed and packed; // // Draw // BeginDrawing(); ClearBackground(RAYWHITE); // // Widgets: buttons, ... // if (GuiButton((Rectangle){padding, padding, b_size.x, b_size.y}, "Add Box")) { bboxes.push_back(freling::BoundingBox({0, 0, 64, 64})); } if (GuiButton((Rectangle){padding * 2 + b_size.x, padding, b_size.x, b_size.y}, "Pack Rects")) { a2_processed = false; std::optional f_packed = freling::pack(*in_frame, bboxes, packed_bboxes); // Update mask in_mask.fill(false); for (const auto& b : bboxes) { for (int j = b.y; j < b.y + b.height; ++j) { for (int i = b.x; i < b.x + b.width; ++i) { in_mask.set(i, j, true); } } }; if (packed) { UnloadTexture(pack_texture); UnloadImage(pack_img); } pack_img = image_from_frame(*f_packed); pack_texture = LoadTextureFromImage(pack_img); packed = true; win_size.x = std::max(win_size.x, in_offset.x + in_texture.width + padding * 2 + pack_texture.width); win_size.y = std::max( win_size.y, in_offset.y + std::max(in_texture.height, pack_texture.height) + padding); SetWindowSize(win_size.x, win_size.y); } if (GuiButton((Rectangle){padding + (padding + b_size.x) * 2, padding, b_size.x, b_size.y}, "A2")) { a2_bboxes = freling::A2(packed_bboxes); a2_bboxes_origin = freling::map_to_origin(bboxes, packed_bboxes, a2_bboxes); a2_processed = true; } // // Input image // DrawTexture(in_texture, in_offset.x, in_offset.y, WHITE); DrawRectangleLines(in_offset.x, in_offset.y, in_texture.width, in_texture.height, BLACK); int c = 0; for (const auto& b : bboxes) { const auto& color = bbox_colors[c % bbox_colors.size()]; draw_resizable(b, in_offset, color); ++c; }; c = 0; if (a2_processed) { for (const auto& b : a2_bboxes_origin) { const auto& color = bbox_colors[c % bbox_colors.size()]; draw_region(b, in_offset, color); ++c; }; } // // Packed image // if (packed) { Vector2 pack_offset = {in_offset.x + in_texture.width + padding, in_offset.y}; DrawTexture(pack_texture, pack_offset.x, pack_offset.y, WHITE); DrawRectangleLines(pack_offset.x, pack_offset.y, pack_texture.width, pack_texture.height, BLACK); int c = 0; for (const auto& b : packed_bboxes) { const auto& color = bbox_colors[c % bbox_colors.size()]; const Rectangle rect = rect_from_bbox(b, pack_offset); DrawRectangleLinesEx(rect, 2, color); ++c; }; if (a2_processed) { int c = 0; for (const auto& b : a2_bboxes) { const auto& color = bbox_colors[c % bbox_colors.size()]; draw_region(b, pack_offset, color); ++c; }; } } EndDrawing(); } // Cleanup UnloadTexture(in_texture); UnloadImage(in_img); if (packed) { UnloadTexture(pack_texture); UnloadImage(pack_img); } CloseWindow(); return 0; }