#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 "bounding_box.h" #include "frame.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(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, 3, 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); } Image image_from_frame(const 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 = 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; 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; 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}; int b_moving_idx = -1; int b_resize_idx = -1; 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 (CheckCollisionPointRec(mouse_pos, rect) and 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 (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { b_moving_idx = -1; b_resize_idx = -1; } if (b_moving_idx != -1) { auto& box = bboxes[b_moving_idx]; box.x += mouse_delta.x; box.y += mouse_delta.y; } if (b_resize_idx != -1) { 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); } // // 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")) { std::optional f_packed = pack(*in_frame, bboxes, packed_bboxes); 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); } // // 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(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()]; draw(b, pack_offset, color); ++c; }; } EndDrawing(); } // Cleanup UnloadTexture(in_texture); UnloadImage(in_img); if (packed) { UnloadTexture(pack_texture); UnloadImage(pack_img); } CloseWindow(); return 0; }