diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88b8199..f54e507 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - name: Build Project run: sudo cmake --build . --config Release --target install - name: Test Project - run: ctest + run: ctest -T test -T coverage deploy: runs-on: ubuntu-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index a9b6080..429b573 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(levelz-cpp LANGUAGES C CXX - VERSION 0.1.1 + VERSION 0.2.0 DESCRIPTION "C/C++ implementation of the LevelZ File Format" HOMEPAGE_URL "https://github.com/LevelZ-File/cpp-bindings" ) diff --git a/include/levelz.h b/include/levelz.h index 292bf96..e8c3ebf 100644 --- a/include/levelz.h +++ b/include/levelz.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include @@ -10,6 +12,7 @@ #include "levelz/coordinate.h" #include "levelz/block.h" #include "levelz/level.h" +#include "levelz/matrix.h" using namespace LevelZ; @@ -88,40 +91,14 @@ namespace { static std::vector read2DPoints(const std::string& input) { std::vector points; - const std::regex matrix("[\\[\\]()]"); const std::vector split = splitString(input, "*"); for (std::string s0 : split) { if (s0.empty()) continue; if (s0.rfind('(', 0) == 0 && s0.rfind(']') == s0.size() - 1) { - s0 = std::regex_replace(s0, matrix, ""); - - unsigned int i = 0; - size_t cpos = 0; - std::string s1; - - int x1, x2, y1, y2; - double cx, cy; - - while ((cpos = s0.find(',')) != std::string::npos) { - s1 = s0.substr(0, cpos); - switch (i) { - case 0: x1 = std::stoi(s1); break; - case 1: x2 = std::stoi(s1); break; - case 2: y1 = std::stoi(s1); break; - case 3: y2 = std::stoi(s1); break; - case 4: cx = std::stod(s1); break; - case 5: cy = std::stod(s1); break; - } - - s0.erase(0, cpos + 1); - i++; - } - - for (int x = x1; x < x2; x++) - for (int y = y1; y < y2; y++) - points.push_back(Coordinate2D(cx + x, cy + y)); + for (const Coordinate2D& c : LevelZ::CoordinateMatrix2D::from_string(s0)) + points.push_back(c); } else points.push_back(Coordinate2D::from_string(s0)); } @@ -139,37 +116,8 @@ namespace { if (s0.empty()) continue; if (s0.rfind('(', 0) == 0 && s0.rfind(']') == s0.size() - 1) { - s0 = std::regex_replace(s0, matrix, ""); - - unsigned int i = 0; - size_t cpos = 0; - std::string s1; - - int x1, x2, y1, y2, z1, z2; - double cx, cy, cz; - - while ((cpos = s0.find(',')) != std::string::npos) { - s1 = s0.substr(0, cpos); - switch (i) { - case 0: x1 = std::stoi(s1); break; - case 1: x2 = std::stoi(s1); break; - case 2: y1 = std::stoi(s1); break; - case 3: y2 = std::stoi(s1); break; - case 4: z1 = std::stoi(s1); break; - case 5: z2 = std::stoi(s1); break; - case 6: cx = std::stod(s1); break; - case 7: cy = std::stod(s1); break; - case 8: cz = std::stod(s1); break; - } - - s0.erase(0, cpos + 1); - i++; - } - - for (int x = x1; x < x2; x++) - for (int y = y1; y < y2; y++) - for (int z = z1; z < z2; z++) - points.push_back(Coordinate3D(cx + x, cy + y, cz + z)); + for (const Coordinate3D& c : LevelZ::CoordinateMatrix3D::from_string(s0)) + points.push_back(c); } else points.push_back(Coordinate3D::from_string(s0)); } diff --git a/include/levelz/matrix.h b/include/levelz/matrix.h new file mode 100644 index 0000000..366555f --- /dev/null +++ b/include/levelz/matrix.h @@ -0,0 +1,303 @@ +#pragma once + +#include +#include +#include + +#include "coordinate.h" + +namespace LevelZ { + + /** + * Represents a coordinate matrix. + */ + struct CoordinateMatrix { + /** + * Converts this coordinate matrix to a string. + * @return The string representation of the coordinate. + */ + virtual std::string to_string() const = 0; + }; + + /** + * Represents a 2D matrix of coordinates. + */ + struct CoordinateMatrix2D : CoordinateMatrix { + public: + /** + * The minimum x coordinate of the matrix. + */ + int minX; + + /** + * The maximum x coordinate of the matrix. + */ + int maxX; + + /** + * The minimum y coordinate of the matrix. + */ + int minY; + + /** + * The maximum y coordinate of the matrix. + */ + int maxY; + + /** + * The starting coordinate of the matrix. + */ + LevelZ::Coordinate2D start; + + /** + * Constructs a new CoordinateMatrix2D object with the minimum coordinates at 0. + * @param x The maximum x coordinate of the matrix. + * @param y The maximum y coordinate of the matrix. + * @param start The starting coordinate of the matrix. + */ + CoordinateMatrix2D(int x, int y, LevelZ::Coordinate2D start) : minX(0), maxX(x), minY(0), maxY(y), start(start) {} + + /** + * Constructs a new CoordinateMatrix2D object with the specified minimum and maximum coordinates. + * @param minX The minimum x coordinate of the matrix. + * @param maxX The maximum x coordinate of the matrix. + * @param minY The minimum y coordinate of the matrix. + * @param maxY The maximum y coordinate of the matrix. + * @param start The starting coordinate of the matrix. + */ + CoordinateMatrix2D(int minX, int maxX, int minY, int maxY, LevelZ::Coordinate2D start) : minX(minX), maxX(maxX), minY(minY), maxY(maxY), start(start) {} + + /** + * Gets the coordinates in the matrix. + * @return The coordinates in the matrix. + */ + std::vector getCoordinates() const { + std::vector coordinates; + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + coordinates.push_back(LevelZ::Coordinate2D(x, y)); + } + } + return coordinates; + } + + std::vector::const_iterator begin() const { + return getCoordinates().begin(); + } + + std::vector::const_iterator end() const { + return getCoordinates().end(); + } + + /** + * Checks if two 2D coordinate matrices are equal. + * @param other The other coordinate matrix to compare to. + * @return true if the coordinate matrices are equal, false otherwise. + */ + bool operator==(const CoordinateMatrix2D& other) const { + return minX == other.minX && maxX == other.maxX && minY == other.minY && maxY == other.maxY && start == other.start; + } + + /** + * Checks if two 2D coordinate matrices are not equal. + * @param other The other coordinate matrix to compare to. + * @return true if the coordinate matrices are not equal, false otherwise. + */ + bool operator!=(const CoordinateMatrix2D& other) const { + return minX != other.minX || maxX != other.maxX || minY != other.minY || maxY != other.maxY || start != other.start; + } + + /** + * Converts this coordinate matrix to a string. + * @return The string representation of the coordinate. + */ + std::string to_string() const { + return "(" + std::to_string(minX) + ", " + std::to_string(maxX) + ", " + std::to_string(minY) + ", " + std::to_string(maxY) + ")^" + start.to_string(); + } + + /** + * Converts a string to a 2D coordinate matrix. + * @param str The string to convert. + * @return The 2D coordinate matrix. + */ + static LevelZ::CoordinateMatrix2D from_string(const std::string& str) { + const std::regex matrix("[\\[\\]()]"); + std::string s0 = std::regex_replace(str, matrix, ""); + s0 = s0.replace(s0.find('^'), 1, ", "); + + unsigned int i = 0; + size_t cpos = 0; + std::string s1; + + int x1, x2, y1, y2; + double cx, cy; + + while ((cpos = s0.find(',')) != std::string::npos) { + s1 = s0.substr(0, cpos); + switch (i) { + case 0: x1 = std::stoi(s1); break; + case 1: x2 = std::stoi(s1); break; + case 2: y1 = std::stoi(s1); break; + case 3: y2 = std::stoi(s1); break; + case 4: cx = std::stod(s1); break; + } + + s0.erase(0, cpos + 1); + i++; + } + cy = std::stod(s0); + + return CoordinateMatrix2D(x1, x2, y1, y2, LevelZ::Coordinate2D(cx, cy)); + } + }; + + /** + * Represents a 3D matrix of coordinates. + */ + struct CoordinateMatrix3D : CoordinateMatrix { + public: + /** + * The minimum x coordinate of the matrix. + */ + int minX; + + /** + * The maximum x coordinate of the matrix. + */ + int maxX; + + /** + * The minimum y coordinate of the matrix. + */ + int minY; + + /** + * The maximum y coordinate of the matrix. + */ + int maxY; + + /** + * The minimum z coordinate of the matrix. + */ + int minZ; + + /** + * The maximum z coordinate of the matrix. + */ + int maxZ; + + /** + * The starting coordinate of the matrix. + */ + LevelZ::Coordinate3D start; + + /** + * Constructs a new CoordinateMatrix3D object with the minimum coordinates at 0. + * @param x The maximum x coordinate of the matrix. + * @param y The maximum y coordinate of the matrix. + * @param z The maximum z coordinate of the matrix. + * @param start The starting coordinate of the matrix. + */ + CoordinateMatrix3D(int x, int y, int z, LevelZ::Coordinate3D start) : minX(0), maxX(x), minY(0), maxY(y), minZ(0), maxZ(z), start(start) {} + + /** + * Constructs a new CoordinateMatrix3D object with the specified minimum and maximum coordinates. + * @param minX The minimum x coordinate of the matrix. + * @param maxX The maximum x coordinate of the matrix. + * @param minY The minimum y coordinate of the matrix. + * @param maxY The maximum y coordinate of the matrix. + * @param minZ The minimum z coordinate of the matrix. + * @param maxZ The maximum z coordinate of the matrix. + * @param start The starting coordinate of the matrix. + */ + CoordinateMatrix3D(int minX, int maxX, int minY, int maxY, int minZ, int maxZ, LevelZ::Coordinate3D start) : minX(minX), maxX(maxX), minY(minY), maxY(maxY), minZ(minZ), maxZ(maxZ), start(start) {} + + /** + * Gets the coordinates in the matrix. + * @return The coordinates in the matrix. + */ + std::vector getCoordinates() const { + std::vector coordinates; + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + coordinates.push_back(LevelZ::Coordinate3D(x, y, z)); + } + } + } + return coordinates; + } + + std::vector::const_iterator begin() const { + return getCoordinates().begin(); + } + + std::vector::const_iterator end() const { + return getCoordinates().end(); + } + + /** + * Checks if two 2D coordinate matrices are equal. + * @param other The other coordinate matrix to compare to. + * @return true if the coordinate matrices are equal, false otherwise. + */ + bool operator==(const CoordinateMatrix3D& other) const { + return minX == other.minX && maxX == other.maxX && minY == other.minY && maxY == other.maxY && minZ == other.minZ && maxZ == other.maxZ && start == other.start; + } + + /** + * Checks if two 2D coordinate matrices are not equal. + * @param other The other coordinate matrix to compare to. + * @return true if the coordinate matrices are not equal, false otherwise. + */ + bool operator!=(const CoordinateMatrix3D& other) const { + return minX != other.minX || maxX != other.maxX || minY != other.minY || maxY != other.maxY || minZ != other.minZ || maxZ != other.maxZ || start == other.start; + } + + /** + * Converts this coordinate matrix to a string. + * @return The string representation of the coordinate. + */ + std::string to_string() const { + return "(" + std::to_string(minX) + ", " + std::to_string(maxX) + ", " + std::to_string(minY) + ", " + std::to_string(maxY) + ", " + std::to_string(minZ) + ", " + std::to_string(maxZ) + ")^" + start.to_string(); + } + + /** + * Converts a string to a 3D coordinate matrix. + * @param str The string to convert. + * @return The 3D coordinate matrix. + */ + static LevelZ::CoordinateMatrix3D from_string(const std::string& str) { + const std::regex matrix("[\\[\\]()]"); + std::string s0 = std::regex_replace(str, matrix, ""); + s0 = s0.replace(s0.find('^'), 1, ", "); + + unsigned int i = 0; + size_t cpos = 0; + std::string s1; + + int x1, x2, y1, y2, z1, z2; + double cx, cy, cz; + + while ((cpos = s0.find(',')) != std::string::npos) { + s1 = s0.substr(0, cpos); + switch (i) { + case 0: x1 = std::stoi(s1); break; + case 1: x2 = std::stoi(s1); break; + case 2: y1 = std::stoi(s1); break; + case 3: y2 = std::stoi(s1); break; + case 4: z1 = std::stoi(s1); break; + case 5: z2 = std::stoi(s1); break; + case 6: cx = std::stod(s1); break; + case 7: cy = std::stod(s1); break; + } + + s0.erase(0, cpos + 1); + i++; + } + cz = std::stod(s0); + + return CoordinateMatrix3D(x1, x2, y1, y2, z1, z2, LevelZ::Coordinate3D(cx, cy, cz)); + } + }; +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0408ae4..0e206cc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,8 +9,12 @@ function(add_test_executable name) add_executable(levelz-test-${name} "src/${name}.cpp" "src/test.h") target_include_directories(levelz-test-${name} PRIVATE "../include") add_test(NAME ${name} COMMAND levelz-test-${name}) + + target_compile_options(levelz-test-${name} PRIVATE -coverage) + target_link_options(levelz-test-${name} PRIVATE -coverage) endfunction() add_test_executable("coordinate") add_test_executable("block") -add_test_executable("level") \ No newline at end of file +add_test_executable("level") +add_test_executable("matrix") \ No newline at end of file diff --git a/test/src/matrix.cpp b/test/src/matrix.cpp new file mode 100644 index 0000000..cb29b3a --- /dev/null +++ b/test/src/matrix.cpp @@ -0,0 +1,24 @@ +#include + +#include "test.h" +#include "levelz.h" + +int main() { + int r = 0; + r |= assert(LevelZ::CoordinateMatrix2D(2, 2, LevelZ::Coordinate2D()) == LevelZ::CoordinateMatrix2D(0, 2, 0, 2, LevelZ::Coordinate2D())); + + // coordinate size + LevelZ::CoordinateMatrix2D matrix2d = LevelZ::CoordinateMatrix2D(2, 2, LevelZ::Coordinate2D()); + r |= assert(matrix2d.getCoordinates().size() == 9); + r |= assert(matrix2d.start == LevelZ::Coordinate2D()); + + LevelZ::CoordinateMatrix3D matrix3d = LevelZ::CoordinateMatrix3D(2, 2, 2, LevelZ::Coordinate3D()); + r |= assert(matrix3d.getCoordinates().size() == 27); + r |= assert(matrix3d.start == LevelZ::Coordinate3D()); + + // #from_string + r |= assert(LevelZ::CoordinateMatrix2D::from_string("(0, 3, 0, 3)^[-1, 2]") == LevelZ::CoordinateMatrix2D(0, 3, 0, 3, LevelZ::Coordinate2D(-1, 2))); + r |= assert(LevelZ::CoordinateMatrix3D::from_string("(0, 3, 0, 3, 0, 3)^[-1, 2, 3]") == LevelZ::CoordinateMatrix3D(0, 3, 0, 3, 0, 3, LevelZ::Coordinate3D(-1, 2, 3))); + + return r; +} \ No newline at end of file