diff --git a/README.md b/README.md index 233682d..b1b5ff3 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,11 @@ The subject is available here: [Test Algo](./test_algo.pdf) ## Notes ### Question 1 + +This is a packing problem, as described on [Wikipedia][1]. +Since this is NP-hard, we know the "exact" solution might be unreachable but we +could find a solution that is good enough for our needs. + ### Question 2 ### Question 3 @@ -30,3 +35,7 @@ I use 2 external libraries for better visualization: I don't rely on them for the algorithm implementation and the core of the exercise doesn't rely on external libraries. + +## References + +[1]: https://en.wikipedia.org/wiki/Packing_problems#Packing_of_rectangles diff --git a/justfile b/justfile index ca30572..3536170 100644 --- a/justfile +++ b/justfile @@ -6,7 +6,7 @@ build: tup run: build - {{exe}} + {{exe}} lenna.png 0 0 0 0 generate-build: git clean -xf src/ diff --git a/lenna.png b/lenna.png new file mode 100644 index 0000000..59ef68a Binary files /dev/null and b/lenna.png differ diff --git a/src/Tupfile b/src/Tupfile index c9d07f1..276acb3 100644 --- a/src/Tupfile +++ b/src/Tupfile @@ -1,9 +1,9 @@ -CPPFLAGS_ = -Wall -Werror -std=c++17 -fcolor-diagnostics -g -O1 +CPPFLAGS_ = -Wall -Werror -std=c++17 -fcolor-diagnostics -g -O1 -I./3rd-party/stb CPPFLAGS_ASAN = $(CPPFLAGS_) -fsanitize=address -fno-omit-frame-pointer CPPFLAGS_THREAD = $(CPPFLAGS_) -fsanitize=thread CPPFLAGS_MEM = $(CPPFLAGS_) -fsanitize=memory -fno-omit-frame-pointer CPPFLAGS_UB = $(CPPFLAGS_) -fsanitize=undefined CPPFLAGS = $(CPPFLAGS_) -: foreach *.cpp |> clang++ -c %f -o %o $(CPPFLAGS) |> %B.o {objs} -: {objs} |> clang++ %f -o %o $(CPPFLAGS) -lpthread |> ../packing +: foreach *.cpp |> ^ CXX %f^ clang++ -c %f -o %o $(CPPFLAGS) |> %B.o {objs} +: {objs} |> ^ LINK %o^ clang++ %f -o %o $(CPPFLAGS) -lpthread |> ../packing diff --git a/src/bounding_box.h b/src/bounding_box.h new file mode 100644 index 0000000..a982cd1 --- /dev/null +++ b/src/bounding_box.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +struct BoundingBox { + uint32_t left; + uint32_t top; + uint32_t right; + uint32_t bottom; +}; diff --git a/src/frame.cpp b/src/frame.cpp new file mode 100644 index 0000000..a979b7f --- /dev/null +++ b/src/frame.cpp @@ -0,0 +1,35 @@ +#include "frame.h" + +#include + +Frame::Frame(uint32_t width, uint32_t height) : width(width), height(height) { + data = new Pixel[width * height]; +} + +Frame::Frame(const Frame& other) : Frame(other.width, other.height) { + std::memcpy(data, other.data, width * height); +} + +Frame& Frame::operator=(const Frame& other) { + width = other.width; + height = other.height; + delete[] data; + data = new Pixel[width * height]; + std::memcpy(data, other.data, width * height); + return *this; +} + +Frame::Frame(Frame&& other) { + width = other.width; + height = other.height; + data = other.data; + other.data = nullptr; +} + +Frame::~Frame() { + delete[] data; +} + +void Frame::fill(uint8_t p) { + std::memset(data, p, width * height * sizeof(Pixel)); +} diff --git a/src/frame.h b/src/frame.h new file mode 100644 index 0000000..44ea2ac --- /dev/null +++ b/src/frame.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +struct Pixel { + uint32_t r; + uint32_t g; + uint32_t b; +}; + +struct Frame { + uint32_t width; + uint32_t height; + Pixel* data; + + Frame(uint32_t width, uint32_t height); + Frame(const Frame& other); + Frame& operator=(const Frame& other); + Frame(Frame&& other); + ~Frame(); + + void fill(uint8_t p); +}; diff --git a/src/main_cli.cpp b/src/main_cli.cpp index 398558b..22b8eb5 100644 --- a/src/main_cli.cpp +++ b/src/main_cli.cpp @@ -1,5 +1,10 @@ #include #include +#include + +#include "bounding_box.h" +#include "pack.h" +#include "png.h" namespace fs = std::filesystem; @@ -20,5 +25,25 @@ int main(int argc, const char* argv[]) { return 1; } + const std::optional in_frame = load_png(input); + if (!in_frame) { + std::cerr << "Cannot load file " << input << "\n"; + return 1; + } + + int i = 2; + std::vector bboxes; + bboxes.reserve((argc - 2) / 4); + while (i < argc) { + const uint32_t x1 = atoi(argv[i]); + const uint32_t y1 = atoi(argv[i + 1]); + const uint32_t x2 = atoi(argv[i + 2]); + const uint32_t y2 = atoi(argv[i + 3]); + bboxes.push_back(BoundingBox({x1, y1, x2, y2})); + i += 4; + } + + Frame regions = pack(*in_frame, bboxes); + return 0; } diff --git a/src/pack.cpp b/src/pack.cpp new file mode 100644 index 0000000..4fd03a5 --- /dev/null +++ b/src/pack.cpp @@ -0,0 +1,6 @@ +#include "pack.h" + +Frame pack(const Frame& in_frame, const std::vector& bboxes) { + // TODO + return Frame(1, 1); +} diff --git a/src/pack.h b/src/pack.h new file mode 100644 index 0000000..d6cbf2c --- /dev/null +++ b/src/pack.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +#include "bounding_box.h" +#include "frame.h" + +Frame pack(const Frame& in_frame, const std::vector& bboxes); diff --git a/src/png.cpp b/src/png.cpp new file mode 100644 index 0000000..f507c91 --- /dev/null +++ b/src/png.cpp @@ -0,0 +1,25 @@ +#include "png.h" + +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +std::optional load_png(const std::filesystem::path& in) { + int x = 0; + int y = 0; + int channels = 0; + const int desired_channels = 3; + unsigned char* data = + stbi_load(in.c_str(), &x, &y, &channels, desired_channels); + + if (data == NULL) { + return {}; + } + + Frame f(x, y); + std::memcpy(f.data, data, x * y * desired_channels); + stbi_image_free(data); + return f; +} diff --git a/src/png.h b/src/png.h new file mode 100644 index 0000000..3521d89 --- /dev/null +++ b/src/png.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +#include "frame.h" + +std::optional load_png(const std::filesystem::path& in);