From 824199f25ab861b01d09d18ec2e17929ede50aaf Mon Sep 17 00:00:00 2001 From: Bart Louwers Date: Mon, 15 May 2023 19:42:30 +0200 Subject: [PATCH] Linux tests + coverage report with Bazel (#1139) --- .bazelrc | 1 + .bazelversion | 2 +- .github/workflows/ios-ci.yml | 4 +- .github/workflows/linux-ci.yml | 43 ++++ BUILD.bazel | 34 ++- bazel/flags.bzl | 18 +- platform/default/BUILD.bazel | 42 +++- platform/linux/BUILD.bazel | 32 +++ src/mbgl/util/grid_index.cpp | 308 ------------------------- src/mbgl/util/grid_index.hpp | 308 +++++++++++++++++++++++++ test/BUILD.bazel | 59 +++++ test/storage/sync_file_source.test.cpp | 9 + test/util/grid_index.test.cpp | 1 - vendor/BUILD.bazel | 19 ++ 14 files changed, 556 insertions(+), 324 deletions(-) create mode 100644 .bazelrc create mode 100644 platform/linux/BUILD.bazel create mode 100644 test/BUILD.bazel diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 000000000..2a1c6ed1f --- /dev/null +++ b/.bazelrc @@ -0,0 +1 @@ +coverage --experimental_ui_max_stdouterr_bytes=10485760 \ No newline at end of file diff --git a/.bazelversion b/.bazelversion index 132c6def5..4ac4fded4 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -6.1.1 \ No newline at end of file +6.2.0 \ No newline at end of file diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index e3e5c2fb7..3f295a2cb 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -139,8 +139,8 @@ jobs: - name: Build app for size test & output size working-directory: ./ run: | - bazelisk build --compilation_mode=opt //platform/ios:size - cp $(bazel cquery --compilation_mode=opt --output=files //platform/ios:size) ./size + bazel build --compilation_mode=opt //platform/ios:size --//:maplibre_platform=ios + cp $(bazel cquery --compilation_mode=opt --output=files //platform/ios:size --//:maplibre_platform=ios) ./size - name: Upload size test result uses: actions/upload-artifact@v3 diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 00047310b..0f873cd9f 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -154,3 +154,46 @@ jobs: path: | metrics/linux-gcc8-release-style.html ./pr_number + + coverage: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + fetch-depth: 0 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libcurl4-openssl-dev \ + libuv1-dev \ + libjpeg-dev \ + libpng-dev \ + libglfw3-dev + + - name: Cache Bazel + uses: actions/cache@v2 + with: + key: ${{ runner.os }}-bazel-${{ hashFiles('.bazelversion', '.bazelrc', 'WORKSPACE', 'WORKSPACE.bazel', 'MODULE.bazel') }} + restore-keys: | + ${{ runner.os }}-bazel- + path: ~/.cache/bazel + + - name: Start server + run: | + npm install --ignore-scripts + node test/storage/server.js & + + - name: Generate coverage report + run: | + bazel coverage --test_output=all --combined_report=lcov --instrumentation_filter="//:mbgl-core" //test:core --//:maplibre_platform=linux --run_under="xvfb-run -a" + echo coverage_report=$(bazel info output_path)/_coverage/_coverage_report.dat >> $GITHUB_ENV + + - name: Upload coverage report + uses: codecov/codecov-action@v3 + with: + files: ${{ env.coverage_report }} + fail_ci_if_error: true + verbose: true diff --git a/BUILD.bazel b/BUILD.bazel index eab0cfba0..eb83ca5d0 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,4 +1,5 @@ load("//bazel:flags.bzl", "CPP_FLAGS", "MAPLIBRE_FLAGS") +load("@bazel_skylib//rules:common_settings.bzl", "string_flag") # This is defined by both # platform/default/filesource-files.json @@ -34,7 +35,6 @@ cc_library( "//vendor:csscolorparser", "//vendor:earcut.hpp", "//vendor:eternal", - "//vendor:icu", "//vendor:mapbox-base", "//vendor:parsedate", "//vendor:polylabel", @@ -42,7 +42,12 @@ cc_library( "//vendor:unique_resource", "//vendor:vector-tile", "//vendor:wagyu", - ], + ] + select({ + "//:ios": [ + "//vendor:icu", + ], + "//:linux": [], + }), ) genrule( @@ -55,6 +60,31 @@ genrule( visibility = ["//visibility:public"], ) +# The next three rules are a bit of a hack +# they are needed until rules_apple has platforms support +# https://github.com/bazelbuild/rules_apple/issues/1658 +# Allows passing a command line flag to set the Platform +# bazel build [target] --//:maplibre_platform=ios + +string_flag( + name = "maplibre_platform", + build_setting_default = "ios", +) + +config_setting( + name = "linux", + flag_values = { + ":maplibre_platform": "linux", + }, +) + +config_setting( + name = "ios", + flag_values = { + ":maplibre_platform": "ios", + }, +) + exports_files( ["LICENSE.md"], visibility = ["//visibility:public"], diff --git a/bazel/flags.bzl b/bazel/flags.bzl index c04c14667..1a8d6f535 100644 --- a/bazel/flags.bzl +++ b/bazel/flags.bzl @@ -8,16 +8,20 @@ WARNING_FLAGS = [ "-Wall", "-Werror", "-Wextra", - "-Wno-c++11-narrowing", - "-Wno-gnu-anonymous-struct", - "-Wno-pointer-to-int-cast", - "-Wno-tautological-constant-compare", "-Wno-unused-parameter", "-Wno-unused-variable", "-Wno-variadic-macros", - "-Wno-nested-anon-types", - "-Wno-newline-eof", -] +] + select({ + "//:ios": [ + "-Wno-newline-eof", + "-Wno-nested-anon-types", + "-Wno-c++11-narrowing", + "-Wno-pointer-to-int-cast", + "-Wno-tautological-constant-compare", + "-Wno-gnu-anonymous-struct", + ], + "//:linux": [], +}) """ Compilation flags used for all .cpp and .mm targets. diff --git a/platform/default/BUILD.bazel b/platform/default/BUILD.bazel index 69e49ce3c..07b498b75 100644 --- a/platform/default/BUILD.bazel +++ b/platform/default/BUILD.bazel @@ -1,5 +1,17 @@ load("//bazel:flags.bzl", "CPP_FLAGS", "MAPLIBRE_FLAGS") +cc_library( + name = "default-collator", + srcs = [ + "src/mbgl/i18n/collator.cpp", + ], + copts = CPP_FLAGS + MAPLIBRE_FLAGS, + deps = [ + "//:mbgl-core", + "//vendor:nunicode", + ], +) + # From: platform/ios/core-files.json # Mostly platform/default cc_library( @@ -29,7 +41,24 @@ cc_library( "src/mbgl/util/png_writer.cpp", "src/mbgl/util/thread_local.cpp", "src/mbgl/util/utf.cpp", - ], + ] + select({ + "//:linux": [ + "src/mbgl/i18n/number_format.cpp", + "src/mbgl/layermanager/layer_manager.cpp", + "src/mbgl/storage/http_file_source.cpp", + "src/mbgl/text/local_glyph_rasterizer.cpp", + "src/mbgl/util/async_task.cpp", + "src/mbgl/util/image.cpp", + "src/mbgl/util/jpeg_reader.cpp", + "src/mbgl/util/logging_stderr.cpp", + "src/mbgl/util/png_reader.cpp", + "src/mbgl/util/run_loop.cpp", + "src/mbgl/util/string_stdlib.cpp", + "src/mbgl/util/thread.cpp", + "src/mbgl/util/timer.cpp", + ], + "//:ios": [], + }), hdrs = [ "include/mbgl/gfx/headless_backend.hpp", "include/mbgl/gfx/headless_frontend.hpp", @@ -51,9 +80,16 @@ cc_library( visibility = [ "//platform/darwin:__pkg__", "//platform/ios:__pkg__", + "//platform/linux:__pkg__", ], deps = [ "//:mbgl-core", - "//platform/darwin:darwin-loop", - ], + ] + select({ + "//:ios": [ + "//platform/darwin:darwin-loop", + ], + "//:linux": [ + "default-collator", + ], + }), ) diff --git a/platform/linux/BUILD.bazel b/platform/linux/BUILD.bazel new file mode 100644 index 000000000..b7b885b90 --- /dev/null +++ b/platform/linux/BUILD.bazel @@ -0,0 +1,32 @@ +load("//bazel:flags.bzl", "CPP_FLAGS", "MAPLIBRE_FLAGS") + +ICU_FLAGS = [ + "-licuuc ", + "-licui18n", + "-licudata", +] + +cc_library( + name = "impl", + srcs = [ + "src/gl_functions.cpp", + "src/headless_backend_egl.cpp", + ], + copts = CPP_FLAGS + MAPLIBRE_FLAGS, + linkopts = [ + "-lGL", + "-lglfw", + "-lEGL", + "-lsqlite3", + "-luv", + "-lz", + "-lcurl", + "-lpng", + "-ljpeg", + ] + ICU_FLAGS, + visibility = ["//visibility:public"], + deps = [ + "//:mbgl-core", + "//platform/default:mbgl-default", + ], +) diff --git a/src/mbgl/util/grid_index.cpp b/src/mbgl/util/grid_index.cpp index fa24e33f3..a5b704c2d 100644 --- a/src/mbgl/util/grid_index.cpp +++ b/src/mbgl/util/grid_index.cpp @@ -1,316 +1,8 @@ #include #include -#include - -#include -#include namespace mbgl { -template -GridIndex::GridIndex(const float width_, const float height_, const uint32_t cellSize_) - : width(width_), - height(height_), - xCellCount(static_cast(std::ceil(width / cellSize_))), - yCellCount(static_cast(std::ceil(height / cellSize_))), - xScale(xCellCount / width), - yScale(yCellCount / height) { - assert(width > 0.0f); - assert(height > 0.0f); - boxCells.resize(xCellCount * yCellCount); - circleCells.resize(xCellCount * yCellCount); -} - -template -void GridIndex::insert(T&& t, const BBox& bbox) { - size_t uid = boxElements.size(); - - auto cx1 = convertToXCellCoord(bbox.min.x); - auto cy1 = convertToYCellCoord(bbox.min.y); - auto cx2 = convertToXCellCoord(bbox.max.x); - auto cy2 = convertToYCellCoord(bbox.max.y); - - std::size_t x; - std::size_t y; - std::size_t cellIndex; - for (x = cx1; x <= cx2; ++x) { - for (y = cy1; y <= cy2; ++y) { - cellIndex = xCellCount * y + x; - boxCells[cellIndex].push_back(uid); - } - } - - boxElements.emplace_back(t, bbox); -} - -template -void GridIndex::insert(T&& t, const BCircle& bcircle) { - size_t uid = circleElements.size(); - - auto cx1 = convertToXCellCoord(bcircle.center.x - bcircle.radius); - auto cy1 = convertToYCellCoord(bcircle.center.y - bcircle.radius); - auto cx2 = convertToXCellCoord(bcircle.center.x + bcircle.radius); - auto cy2 = convertToYCellCoord(bcircle.center.y + bcircle.radius); - - std::size_t x; - std::size_t y; - std::size_t cellIndex; - for (x = cx1; x <= cx2; ++x) { - for (y = cy1; y <= cy2; ++y) { - cellIndex = xCellCount * y + x; - circleCells[cellIndex].push_back(uid); - } - } - - circleElements.emplace_back(t, bcircle); -} - -template -std::vector GridIndex::query(const BBox& queryBBox) const { - std::vector result; - query(queryBBox, [&](const T& t, const BBox&) -> bool { - result.push_back(t); - return false; - }); - return result; -} - -template -std::vector::BBox>> GridIndex::queryWithBoxes(const BBox& queryBBox) const { - std::vector> result; - query(queryBBox, [&](const T& t, const BBox& bbox) -> bool { - result.push_back(std::make_pair(t, bbox)); - return false; - }); - return result; -} - -template -bool GridIndex::hitTest(const BBox& queryBBox, std::optional> predicate) const { - bool hit = false; - query(queryBBox, [&](const T& t, const BBox&) -> bool { - if (!predicate || (*predicate)(t)) { - hit = true; - return true; - } else { - return false; - } - }); - return hit; -} - -template -bool GridIndex::hitTest(const BCircle& queryBCircle, std::optional> predicate) const { - bool hit = false; - query(queryBCircle, [&](const T& t, const BBox&) -> bool { - if (!predicate || (*predicate)(t)) { - hit = true; - return true; - } else { - return false; - } - }); - return hit; -} - -template -bool GridIndex::noIntersection(const BBox& queryBBox) const { - return queryBBox.max.x < 0 || queryBBox.min.x >= width || queryBBox.max.y < 0 || queryBBox.min.y >= height; -} - -template -bool GridIndex::completeIntersection(const BBox& queryBBox) const { - return queryBBox.min.x <= 0 && queryBBox.min.y <= 0 && width <= queryBBox.max.x && height <= queryBBox.max.y; -} - -template -typename GridIndex::BBox GridIndex::convertToBox(const BCircle& circle) const { - return BBox{{circle.center.x - circle.radius, circle.center.y - circle.radius}, - {circle.center.x + circle.radius, circle.center.y + circle.radius}}; -} - -template -void GridIndex::query(const BBox& queryBBox, std::function resultFn) const { - std::unordered_set seenBoxes; - std::unordered_set seenCircles; - - if (noIntersection(queryBBox)) { - return; - } else if (completeIntersection(queryBBox)) { - for (auto& element : boxElements) { - if (resultFn(element.first, element.second)) { - return; - } - } - for (auto& element : circleElements) { - if (resultFn(element.first, convertToBox(element.second))) { - return; - } - } - return; - } - - auto cx1 = convertToXCellCoord(queryBBox.min.x); - auto cy1 = convertToYCellCoord(queryBBox.min.y); - auto cx2 = convertToXCellCoord(queryBBox.max.x); - auto cy2 = convertToYCellCoord(queryBBox.max.y); - - std::size_t x; - std::size_t y; - std::size_t cellIndex; - for (x = cx1; x <= cx2; ++x) { - for (y = cy1; y <= cy2; ++y) { - cellIndex = xCellCount * y + x; - // Look up other boxes - for (auto uid : boxCells[cellIndex]) { - if (seenBoxes.count(uid) == 0) { - seenBoxes.insert(uid); - - auto& pair = boxElements.at(uid); - auto& bbox = pair.second; - if (boxesCollide(queryBBox, bbox)) { - if (resultFn(pair.first, bbox)) { - return; - } - } - } - } - - // Look up circles - for (auto uid : circleCells[cellIndex]) { - if (seenCircles.count(uid) == 0) { - seenCircles.insert(uid); - - auto& pair = circleElements.at(uid); - auto& bcircle = pair.second; - if (circleAndBoxCollide(bcircle, queryBBox)) { - if (resultFn(pair.first, convertToBox(bcircle))) { - return; - } - } - } - } - } - } -} - -template -void GridIndex::query(const BCircle& queryBCircle, std::function resultFn) const { - std::unordered_set seenBoxes; - std::unordered_set seenCircles; - - BBox queryBBox = convertToBox(queryBCircle); - if (noIntersection(queryBBox)) { - return; - } else if (completeIntersection(queryBBox)) { - for (auto& element : boxElements) { - if (resultFn(element.first, element.second)) { - return; - } - } - for (auto& element : circleElements) { - if (resultFn(element.first, convertToBox(element.second))) { - return; - } - } - } - - auto cx1 = convertToXCellCoord(queryBCircle.center.x - queryBCircle.radius); - auto cy1 = convertToYCellCoord(queryBCircle.center.y - queryBCircle.radius); - auto cx2 = convertToXCellCoord(queryBCircle.center.x + queryBCircle.radius); - auto cy2 = convertToYCellCoord(queryBCircle.center.y + queryBCircle.radius); - - std::size_t x; - std::size_t y; - std::size_t cellIndex; - for (x = cx1; x <= cx2; ++x) { - for (y = cy1; y <= cy2; ++y) { - cellIndex = xCellCount * y + x; - // Look up boxes - for (auto uid : boxCells[cellIndex]) { - if (seenBoxes.count(uid) == 0) { - seenBoxes.insert(uid); - - auto& pair = boxElements.at(uid); - auto& bbox = pair.second; - if (circleAndBoxCollide(queryBCircle, bbox)) { - if (resultFn(pair.first, bbox)) { - return; - } - } - } - } - - // Look up other circles - for (auto uid : circleCells[cellIndex]) { - if (seenCircles.count(uid) == 0) { - seenCircles.insert(uid); - - auto& pair = circleElements.at(uid); - auto& bcircle = pair.second; - if (circlesCollide(queryBCircle, bcircle)) { - if (resultFn(pair.first, convertToBox(bcircle))) { - return; - } - } - } - } - } - } -} - -template -std::size_t GridIndex::convertToXCellCoord(const float x) const { - return static_cast(util::max(0.0, util::min(xCellCount - 1.0, std::floor(x * xScale)))); -} - -template -std::size_t GridIndex::convertToYCellCoord(const float y) const { - return static_cast(util::max(0.0, util::min(yCellCount - 1.0, std::floor(y * yScale)))); -} - -template -bool GridIndex::boxesCollide(const BBox& first, const BBox& second) const { - return first.min.x <= second.max.x && first.min.y <= second.max.y && first.max.x >= second.min.x && - first.max.y >= second.min.y; -} - -template -bool GridIndex::circlesCollide(const BCircle& first, const BCircle& second) const { - auto dx = second.center.x - first.center.x; - auto dy = second.center.y - first.center.y; - auto bothRadii = first.radius + second.radius; - return (bothRadii * bothRadii) > (dx * dx + dy * dy); -} - -template -bool GridIndex::circleAndBoxCollide(const BCircle& circle, const BBox& box) const { - auto halfRectWidth = (box.max.x - box.min.x) / 2; - auto distX = std::abs(circle.center.x - (box.min.x + halfRectWidth)); - if (distX > (halfRectWidth + circle.radius)) { - return false; - } - - auto halfRectHeight = (box.max.y - box.min.y) / 2; - auto distY = std::abs(circle.center.y - (box.min.y + halfRectHeight)); - if (distY > (halfRectHeight + circle.radius)) { - return false; - } - - if (distX <= halfRectWidth || distY <= halfRectHeight) { - return true; - } - - auto dx = distX - halfRectWidth; - auto dy = distY - halfRectHeight; - return (dx * dx + dy * dy) <= (circle.radius * circle.radius); -} - -template -bool GridIndex::empty() const { - return boxElements.empty() && circleElements.empty(); -} - template class GridIndex; } // namespace mbgl diff --git a/src/mbgl/util/grid_index.hpp b/src/mbgl/util/grid_index.hpp index 455ca1968..3cbd9ae9f 100644 --- a/src/mbgl/util/grid_index.hpp +++ b/src/mbgl/util/grid_index.hpp @@ -2,12 +2,16 @@ #include #include +#include +#include +#include #include #include #include #include #include +#include namespace mbgl { @@ -98,4 +102,308 @@ private: std::vector> circleCells; }; +template +GridIndex::GridIndex(const float width_, const float height_, const uint32_t cellSize_) + : width(width_), + height(height_), + xCellCount(static_cast(std::ceil(width / cellSize_))), + yCellCount(static_cast(std::ceil(height / cellSize_))), + xScale(xCellCount / width), + yScale(yCellCount / height) { + assert(width > 0.0f); + assert(height > 0.0f); + boxCells.resize(xCellCount * yCellCount); + circleCells.resize(xCellCount * yCellCount); +} + +template +void GridIndex::insert(T&& t, const BBox& bbox) { + size_t uid = boxElements.size(); + + auto cx1 = convertToXCellCoord(bbox.min.x); + auto cy1 = convertToYCellCoord(bbox.min.y); + auto cx2 = convertToXCellCoord(bbox.max.x); + auto cy2 = convertToYCellCoord(bbox.max.y); + + std::size_t x; + std::size_t y; + std::size_t cellIndex; + for (x = cx1; x <= cx2; ++x) { + for (y = cy1; y <= cy2; ++y) { + cellIndex = xCellCount * y + x; + boxCells[cellIndex].push_back(uid); + } + } + + boxElements.emplace_back(t, bbox); +} + +template +void GridIndex::insert(T&& t, const BCircle& bcircle) { + size_t uid = circleElements.size(); + + auto cx1 = convertToXCellCoord(bcircle.center.x - bcircle.radius); + auto cy1 = convertToYCellCoord(bcircle.center.y - bcircle.radius); + auto cx2 = convertToXCellCoord(bcircle.center.x + bcircle.radius); + auto cy2 = convertToYCellCoord(bcircle.center.y + bcircle.radius); + + std::size_t x; + std::size_t y; + std::size_t cellIndex; + for (x = cx1; x <= cx2; ++x) { + for (y = cy1; y <= cy2; ++y) { + cellIndex = xCellCount * y + x; + circleCells[cellIndex].push_back(uid); + } + } + + circleElements.emplace_back(t, bcircle); +} + +template +std::vector GridIndex::query(const BBox& queryBBox) const { + std::vector result; + query(queryBBox, [&](const T& t, const BBox&) -> bool { + result.push_back(t); + return false; + }); + return result; +} + +template +std::vector::BBox>> GridIndex::queryWithBoxes(const BBox& queryBBox) const { + std::vector> result; + query(queryBBox, [&](const T& t, const BBox& bbox) -> bool { + result.push_back(std::make_pair(t, bbox)); + return false; + }); + return result; +} + +template +bool GridIndex::hitTest(const BBox& queryBBox, std::optional> predicate) const { + bool hit = false; + query(queryBBox, [&](const T& t, const BBox&) -> bool { + if (!predicate || (*predicate)(t)) { + hit = true; + return true; + } else { + return false; + } + }); + return hit; +} + +template +bool GridIndex::hitTest(const BCircle& queryBCircle, std::optional> predicate) const { + bool hit = false; + query(queryBCircle, [&](const T& t, const BBox&) -> bool { + if (!predicate || (*predicate)(t)) { + hit = true; + return true; + } else { + return false; + } + }); + return hit; +} + +template +bool GridIndex::noIntersection(const BBox& queryBBox) const { + return queryBBox.max.x < 0 || queryBBox.min.x >= width || queryBBox.max.y < 0 || queryBBox.min.y >= height; +} + +template +bool GridIndex::completeIntersection(const BBox& queryBBox) const { + return queryBBox.min.x <= 0 && queryBBox.min.y <= 0 && width <= queryBBox.max.x && height <= queryBBox.max.y; +} + +template +typename GridIndex::BBox GridIndex::convertToBox(const BCircle& circle) const { + return BBox{{circle.center.x - circle.radius, circle.center.y - circle.radius}, + {circle.center.x + circle.radius, circle.center.y + circle.radius}}; +} + +template +void GridIndex::query(const BBox& queryBBox, std::function resultFn) const { + std::unordered_set seenBoxes; + std::unordered_set seenCircles; + + if (noIntersection(queryBBox)) { + return; + } else if (completeIntersection(queryBBox)) { + for (auto& element : boxElements) { + if (resultFn(element.first, element.second)) { + return; + } + } + for (auto& element : circleElements) { + if (resultFn(element.first, convertToBox(element.second))) { + return; + } + } + return; + } + + auto cx1 = convertToXCellCoord(queryBBox.min.x); + auto cy1 = convertToYCellCoord(queryBBox.min.y); + auto cx2 = convertToXCellCoord(queryBBox.max.x); + auto cy2 = convertToYCellCoord(queryBBox.max.y); + + std::size_t x; + std::size_t y; + std::size_t cellIndex; + for (x = cx1; x <= cx2; ++x) { + for (y = cy1; y <= cy2; ++y) { + cellIndex = xCellCount * y + x; + // Look up other boxes + for (auto uid : boxCells[cellIndex]) { + if (seenBoxes.count(uid) == 0) { + seenBoxes.insert(uid); + + auto& pair = boxElements.at(uid); + auto& bbox = pair.second; + if (boxesCollide(queryBBox, bbox)) { + if (resultFn(pair.first, bbox)) { + return; + } + } + } + } + + // Look up circles + for (auto uid : circleCells[cellIndex]) { + if (seenCircles.count(uid) == 0) { + seenCircles.insert(uid); + + auto& pair = circleElements.at(uid); + auto& bcircle = pair.second; + if (circleAndBoxCollide(bcircle, queryBBox)) { + if (resultFn(pair.first, convertToBox(bcircle))) { + return; + } + } + } + } + } + } +} + +template +void GridIndex::query(const BCircle& queryBCircle, std::function resultFn) const { + std::unordered_set seenBoxes; + std::unordered_set seenCircles; + + BBox queryBBox = convertToBox(queryBCircle); + if (noIntersection(queryBBox)) { + return; + } else if (completeIntersection(queryBBox)) { + for (auto& element : boxElements) { + if (resultFn(element.first, element.second)) { + return; + } + } + for (auto& element : circleElements) { + if (resultFn(element.first, convertToBox(element.second))) { + return; + } + } + } + + auto cx1 = convertToXCellCoord(queryBCircle.center.x - queryBCircle.radius); + auto cy1 = convertToYCellCoord(queryBCircle.center.y - queryBCircle.radius); + auto cx2 = convertToXCellCoord(queryBCircle.center.x + queryBCircle.radius); + auto cy2 = convertToYCellCoord(queryBCircle.center.y + queryBCircle.radius); + + std::size_t x; + std::size_t y; + std::size_t cellIndex; + for (x = cx1; x <= cx2; ++x) { + for (y = cy1; y <= cy2; ++y) { + cellIndex = xCellCount * y + x; + // Look up boxes + for (auto uid : boxCells[cellIndex]) { + if (seenBoxes.count(uid) == 0) { + seenBoxes.insert(uid); + + auto& pair = boxElements.at(uid); + auto& bbox = pair.second; + if (circleAndBoxCollide(queryBCircle, bbox)) { + if (resultFn(pair.first, bbox)) { + return; + } + } + } + } + + // Look up other circles + for (auto uid : circleCells[cellIndex]) { + if (seenCircles.count(uid) == 0) { + seenCircles.insert(uid); + + auto& pair = circleElements.at(uid); + auto& bcircle = pair.second; + if (circlesCollide(queryBCircle, bcircle)) { + if (resultFn(pair.first, convertToBox(bcircle))) { + return; + } + } + } + } + } + } +} + +template +std::size_t GridIndex::convertToXCellCoord(const float x) const { + return static_cast(util::max(0.0, util::min(xCellCount - 1.0, std::floor(x * xScale)))); +} + +template +std::size_t GridIndex::convertToYCellCoord(const float y) const { + return static_cast(util::max(0.0, util::min(yCellCount - 1.0, std::floor(y * yScale)))); +} + +template +bool GridIndex::boxesCollide(const BBox& first, const BBox& second) const { + return first.min.x <= second.max.x && first.min.y <= second.max.y && first.max.x >= second.min.x && + first.max.y >= second.min.y; +} + +template +bool GridIndex::circlesCollide(const BCircle& first, const BCircle& second) const { + auto dx = second.center.x - first.center.x; + auto dy = second.center.y - first.center.y; + auto bothRadii = first.radius + second.radius; + return (bothRadii * bothRadii) > (dx * dx + dy * dy); +} + +template +bool GridIndex::circleAndBoxCollide(const BCircle& circle, const BBox& box) const { + auto halfRectWidth = (box.max.x - box.min.x) / 2; + auto distX = std::abs(circle.center.x - (box.min.x + halfRectWidth)); + if (distX > (halfRectWidth + circle.radius)) { + return false; + } + + auto halfRectHeight = (box.max.y - box.min.y) / 2; + auto distY = std::abs(circle.center.y - (box.min.y + halfRectHeight)); + if (distY > (halfRectHeight + circle.radius)) { + return false; + } + + if (distX <= halfRectWidth || distY <= halfRectHeight) { + return true; + } + + auto dx = distX - halfRectWidth; + auto dy = distY - halfRectHeight; + return (dx * dx + dy * dy) <= (circle.radius * circle.radius); +} + +template +bool GridIndex::empty() const { + return boxElements.empty() && circleElements.empty(); +} + } // namespace mbgl diff --git a/test/BUILD.bazel b/test/BUILD.bazel new file mode 100644 index 000000000..7e6734fb6 --- /dev/null +++ b/test/BUILD.bazel @@ -0,0 +1,59 @@ +load("//bazel:flags.bzl", "CPP_FLAGS", "MAPLIBRE_FLAGS") + +cc_library( + name = "testutils", + hdrs = [ + "include/mbgl/test/util.hpp", + ], + strip_include_prefix = "include", + deps = [ + "//vendor/googletest:gtest", + ], +) + +cc_library( + name = "testlib", + srcs = glob( + [ + "src/mbgl/test/*.cpp", + ], + exclude = [ + "src/mbgl/test/test.cpp", + "src/mbgl/test/http_server.cpp", + ], + ), + hdrs = glob([ + "src/mbgl/test/*.hpp", + ]), + copts = CPP_FLAGS + MAPLIBRE_FLAGS, + strip_include_prefix = "src", + deps = [ + "testutils", + "//:mbgl-core", + ], +) + +cc_test( + name = "core", + srcs = glob( + [ + "**/*.test.cpp", + ], + exclude = [ + "storage/sync_file_source.test.cpp", + "util/timer.test.cpp", + "style/expression/expression.test.cpp", + "map/map.test.cpp", + ], + ), + copts = CPP_FLAGS + MAPLIBRE_FLAGS, + data = glob(["fixtures/**/*"]), + local = True, + deps = [ + "testlib", + "testutils", + "//:mbgl-core", + "//platform/linux:impl", + "//vendor/googletest:gtest_main", + ], +) diff --git a/test/storage/sync_file_source.test.cpp b/test/storage/sync_file_source.test.cpp index 8144cd408..572f4eb4f 100644 --- a/test/storage/sync_file_source.test.cpp +++ b/test/storage/sync_file_source.test.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,14 @@ public: assets.emplace(key, std::make_shared(data)); }; + virtual void setResourceOptions(ResourceOptions _) override { throw std::runtime_error("Not implemented"); } + + virtual ResourceOptions getResourceOptions() override { throw std::runtime_error("Not implemented"); } + + virtual void setClientOptions(ClientOptions) override { throw std::runtime_error("Not implemented"); } + + virtual ClientOptions getClientOptions() { throw std::runtime_error("Not implemented"); } + private: std::unordered_map> assets; }; diff --git a/test/util/grid_index.test.cpp b/test/util/grid_index.test.cpp index dbb5e5372..ef3575b77 100644 --- a/test/util/grid_index.test.cpp +++ b/test/util/grid_index.test.cpp @@ -1,5 +1,4 @@ #include -#include #include diff --git a/vendor/BUILD.bazel b/vendor/BUILD.bazel index 5d4d4692b..c4d05ae04 100644 --- a/vendor/BUILD.bazel +++ b/vendor/BUILD.bazel @@ -194,3 +194,22 @@ cc_library( includes = ["icu/include"], visibility = ["//visibility:public"], ) + +cc_library( + name = "nunicode-gen", + hdrs = glob(["nunicode/src/libnu/gen/*.c"]), + include_prefix = "gen", + strip_include_prefix = "nunicode/src/libnu/gen", +) + +cc_library( + name = "nunicode", + srcs = glob(["nunicode/src/libnu/*.c"]), + hdrs = glob(["nunicode/include/libnu/*.h"]), + include_prefix = "libnu", + strip_include_prefix = "nunicode/include/libnu", + visibility = ["//visibility:public"], + deps = [ + "nunicode-gen", + ], +)