Linux tests + coverage report with Bazel (#1139)
This commit is contained in:
1
.bazelrc
Normal file
1
.bazelrc
Normal file
@@ -0,0 +1 @@
|
||||
coverage --experimental_ui_max_stdouterr_bytes=10485760
|
||||
@@ -1 +1 @@
|
||||
6.1.1
|
||||
6.2.0
|
||||
4
.github/workflows/ios-ci.yml
vendored
4
.github/workflows/ios-ci.yml
vendored
@@ -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
|
||||
|
||||
43
.github/workflows/linux-ci.yml
vendored
43
.github/workflows/linux-ci.yml
vendored
@@ -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
|
||||
|
||||
34
BUILD.bazel
34
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"],
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
32
platform/linux/BUILD.bazel
Normal file
32
platform/linux/BUILD.bazel
Normal file
@@ -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",
|
||||
],
|
||||
)
|
||||
@@ -1,316 +1,8 @@
|
||||
#include <mbgl/util/grid_index.hpp>
|
||||
#include <mbgl/geometry/feature_index.hpp>
|
||||
#include <mbgl/math/minmax.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <cmath>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
template <class T>
|
||||
GridIndex<T>::GridIndex(const float width_, const float height_, const uint32_t cellSize_)
|
||||
: width(width_),
|
||||
height(height_),
|
||||
xCellCount(static_cast<size_t>(std::ceil(width / cellSize_))),
|
||||
yCellCount(static_cast<size_t>(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 <class T>
|
||||
void GridIndex<T>::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 <class T>
|
||||
void GridIndex<T>::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 <class T>
|
||||
std::vector<T> GridIndex<T>::query(const BBox& queryBBox) const {
|
||||
std::vector<T> result;
|
||||
query(queryBBox, [&](const T& t, const BBox&) -> bool {
|
||||
result.push_back(t);
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<std::pair<T, typename GridIndex<T>::BBox>> GridIndex<T>::queryWithBoxes(const BBox& queryBBox) const {
|
||||
std::vector<std::pair<T, BBox>> result;
|
||||
query(queryBBox, [&](const T& t, const BBox& bbox) -> bool {
|
||||
result.push_back(std::make_pair(t, bbox));
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GridIndex<T>::hitTest(const BBox& queryBBox, std::optional<std::function<bool(const T&)>> 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 <class T>
|
||||
bool GridIndex<T>::hitTest(const BCircle& queryBCircle, std::optional<std::function<bool(const T&)>> 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 <class T>
|
||||
bool GridIndex<T>::noIntersection(const BBox& queryBBox) const {
|
||||
return queryBBox.max.x < 0 || queryBBox.min.x >= width || queryBBox.max.y < 0 || queryBBox.min.y >= height;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GridIndex<T>::completeIntersection(const BBox& queryBBox) const {
|
||||
return queryBBox.min.x <= 0 && queryBBox.min.y <= 0 && width <= queryBBox.max.x && height <= queryBBox.max.y;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename GridIndex<T>::BBox GridIndex<T>::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 <class T>
|
||||
void GridIndex<T>::query(const BBox& queryBBox, std::function<bool(const T&, const BBox&)> resultFn) const {
|
||||
std::unordered_set<size_t> seenBoxes;
|
||||
std::unordered_set<size_t> 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 <class T>
|
||||
void GridIndex<T>::query(const BCircle& queryBCircle, std::function<bool(const T&, const BBox&)> resultFn) const {
|
||||
std::unordered_set<size_t> seenBoxes;
|
||||
std::unordered_set<size_t> 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 <class T>
|
||||
std::size_t GridIndex<T>::convertToXCellCoord(const float x) const {
|
||||
return static_cast<size_t>(util::max(0.0, util::min(xCellCount - 1.0, std::floor(x * xScale))));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::size_t GridIndex<T>::convertToYCellCoord(const float y) const {
|
||||
return static_cast<size_t>(util::max(0.0, util::min(yCellCount - 1.0, std::floor(y * yScale))));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GridIndex<T>::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 <class T>
|
||||
bool GridIndex<T>::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 <class T>
|
||||
bool GridIndex<T>::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 <class T>
|
||||
bool GridIndex<T>::empty() const {
|
||||
return boxElements.empty() && circleElements.empty();
|
||||
}
|
||||
|
||||
template class GridIndex<IndexedSubfeature>;
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
|
||||
#include <mapbox/geometry/point.hpp>
|
||||
#include <mapbox/geometry/box.hpp>
|
||||
#include <mbgl/math/minmax.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -98,4 +102,308 @@ private:
|
||||
std::vector<std::vector<size_t>> circleCells;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
GridIndex<T>::GridIndex(const float width_, const float height_, const uint32_t cellSize_)
|
||||
: width(width_),
|
||||
height(height_),
|
||||
xCellCount(static_cast<size_t>(std::ceil(width / cellSize_))),
|
||||
yCellCount(static_cast<size_t>(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 <class T>
|
||||
void GridIndex<T>::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 <class T>
|
||||
void GridIndex<T>::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 <class T>
|
||||
std::vector<T> GridIndex<T>::query(const BBox& queryBBox) const {
|
||||
std::vector<T> result;
|
||||
query(queryBBox, [&](const T& t, const BBox&) -> bool {
|
||||
result.push_back(t);
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<std::pair<T, typename GridIndex<T>::BBox>> GridIndex<T>::queryWithBoxes(const BBox& queryBBox) const {
|
||||
std::vector<std::pair<T, BBox>> result;
|
||||
query(queryBBox, [&](const T& t, const BBox& bbox) -> bool {
|
||||
result.push_back(std::make_pair(t, bbox));
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GridIndex<T>::hitTest(const BBox& queryBBox, std::optional<std::function<bool(const T&)>> 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 <class T>
|
||||
bool GridIndex<T>::hitTest(const BCircle& queryBCircle, std::optional<std::function<bool(const T&)>> 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 <class T>
|
||||
bool GridIndex<T>::noIntersection(const BBox& queryBBox) const {
|
||||
return queryBBox.max.x < 0 || queryBBox.min.x >= width || queryBBox.max.y < 0 || queryBBox.min.y >= height;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GridIndex<T>::completeIntersection(const BBox& queryBBox) const {
|
||||
return queryBBox.min.x <= 0 && queryBBox.min.y <= 0 && width <= queryBBox.max.x && height <= queryBBox.max.y;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename GridIndex<T>::BBox GridIndex<T>::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 <class T>
|
||||
void GridIndex<T>::query(const BBox& queryBBox, std::function<bool(const T&, const BBox&)> resultFn) const {
|
||||
std::unordered_set<size_t> seenBoxes;
|
||||
std::unordered_set<size_t> 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 <class T>
|
||||
void GridIndex<T>::query(const BCircle& queryBCircle, std::function<bool(const T&, const BBox&)> resultFn) const {
|
||||
std::unordered_set<size_t> seenBoxes;
|
||||
std::unordered_set<size_t> 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 <class T>
|
||||
std::size_t GridIndex<T>::convertToXCellCoord(const float x) const {
|
||||
return static_cast<size_t>(util::max(0.0, util::min(xCellCount - 1.0, std::floor(x * xScale))));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::size_t GridIndex<T>::convertToYCellCoord(const float y) const {
|
||||
return static_cast<size_t>(util::max(0.0, util::min(yCellCount - 1.0, std::floor(y * yScale))));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GridIndex<T>::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 <class T>
|
||||
bool GridIndex<T>::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 <class T>
|
||||
bool GridIndex<T>::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 <class T>
|
||||
bool GridIndex<T>::empty() const {
|
||||
return boxElements.empty() && circleElements.empty();
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
59
test/BUILD.bazel
Normal file
59
test/BUILD.bazel
Normal file
@@ -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",
|
||||
],
|
||||
)
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <mbgl/test/map_adapter.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@@ -34,6 +35,14 @@ public:
|
||||
assets.emplace(key, std::make_shared<std::string>(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<std::string, std::shared_ptr<std::string>> assets;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <mbgl/util/grid_index.hpp>
|
||||
#include <mbgl/util/grid_index.cpp>
|
||||
|
||||
#include <mbgl/test/util.hpp>
|
||||
|
||||
|
||||
19
vendor/BUILD.bazel
vendored
19
vendor/BUILD.bazel
vendored
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user