summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAleksandar Stojiljkovic <aleksandar.stojiljkovic@mapbox.com>2019-05-13 23:02:05 +0300
committerAleksandar Stojiljkovic <aleksandar.stojiljkovic@mapbox.com>2019-05-28 14:38:01 +0300
commit9809c9f8f5583739e07d1a02df4c6cb96dfc4a10 (patch)
treef61c9979ad7943c1975aad6725054b2458c22b36 /test
parentddb6f749ec8992c363c067b6ad5871b31ccb7b8a (diff)
downloadqtlocation-mapboxgl-9809c9f8f5583739e07d1a02df4c6cb96dfc4a10.tar.gz
[core] Offset viewport center when edge insets are specified
The change is implemented in TransformState::getProjMatrix, the rest of the code is making sure that existing API contracts stay and there are tests verifyingrendering and render query processing only items within screen and given tolerance around screen edges. MapView: don't bake edge insets into relalculated camera center. Keep edge insets as property of camera in TransformState (similar to pitch, zoom, bearing) independent from specified camera center. Interpolate edge insets in animation. iOS Demo app: "Turn On/Off Content Insets" pitch the camera and navigate to convenient location in Denver, where streets are parallel to cardinal directions, to illustrate viewport center offset when edge insets are set. Tests: ViewFrustumCulling: although Annotations are deprecated, queryRenderedFeatures related tests in Annotations would need to get ported and decided to add the edge insets related query tests next to them. Verify frustum culling (render+queryRenderedFeatures) With different camera and edge insets setups. TODO: port Annotations tests. Transform.Padding: Verify that coordinates take proper place on screen after applying edge insets. LocalGlyphRasterizer: verify text rendering when applying padding. Related to #11882: both use projection matrix elements [8] and [9]. Alternative approach to this was to increase and offset map origin so that the screen would be a sub-rectangle in larger map viewport. This approach has a drawback of unecessary processing the items that are outside screen area. Fixes #12107, #12728, navigation-sdks/issues/120
Diffstat (limited to 'test')
-rw-r--r--test/api/annotations.test.cpp67
-rw-r--r--test/fixtures/local_glyphs/no_local_with_content_insets/expected.pngbin0 -> 8428 bytes
-rw-r--r--test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.pngbin0 -> 7263 bytes
-rw-r--r--test/map/transform.test.cpp45
-rw-r--r--test/text/local_glyph_rasterizer.test.cpp50
5 files changed, 148 insertions, 14 deletions
diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp
index d7cb572cb0..03330dc4c6 100644
--- a/test/api/annotations.test.cpp
+++ b/test/api/annotations.test.cpp
@@ -434,6 +434,73 @@ TEST(Annotations, VisibleFeatures) {
EXPECT_EQ(features.size(), ids.size());
}
+TEST(Annotations, ViewFrustumCulling) {
+ // The purpose of the test is to control that annotations outside screen
+ // rectangle are not rendered for different camera setup, especially when
+ // using edge insets - viewport center is then offsetted.
+
+ // Important premise of this test is "static const float viewportPadding = 100;"
+ // as defined in collision_index.cpp: tests using edge insets are writen so that
+ // padding is 128 (half of viewSize width). If increasing viewportPadding,
+ // increase the padding in test cases below.
+ AnnotationTest test;
+
+ auto viewSize = test.frontend.getSize();
+ auto box = ScreenBox { {}, { double(viewSize.width), double(viewSize.height) } };
+
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.addAnnotationImage(namedMarker("default_marker"));
+ const LatLng center = { 5.0, 5.0 };
+ test.map.jumpTo(CameraOptions().withCenter(center).withZoom(3.0));
+
+ LatLng tl = test.map.latLngForPixel(ScreenCoordinate(0, 0));
+ LatLng br = test.map.latLngForPixel(ScreenCoordinate(viewSize.width, viewSize.height));
+
+ std::vector<LatLng> latLngs = {
+ tl,
+ test.map.latLngForPixel(ScreenCoordinate(viewSize.width, 0)),
+ test.map.latLngForPixel(ScreenCoordinate(0, viewSize.height)),
+ br,
+ center};
+
+ std::vector<mbgl::AnnotationID> ids;
+ for (auto latLng : latLngs) {
+ ids.push_back(test.map.addAnnotation(SymbolAnnotation { { latLng.longitude(), latLng.latitude() }, "default_marker" }));
+ }
+
+ std::vector<std::pair<CameraOptions, std::vector<uint64_t>>> expectedVisibleForCamera = {
+ // Start with all markers visible.
+ { CameraOptions(), { 0, 1, 2, 3, 4 } },
+ // Move center to topLeft: only former center and top left (now center) are visible.
+ { CameraOptions().withCenter(tl), { 0, 4 } },
+ // Reset center. With pitch: only top row markers and center are visible.
+ { CameraOptions().withCenter(center).withPitch(45), { 0, 1, 4 } },
+ // Reset pitch, and use padding to move viewport center: only topleft and center are visible.
+ { CameraOptions().withPitch(0).withPadding(EdgeInsets { viewSize.height * 0.5, viewSize.width * 0.5, 0, 0 }), { 0, 4 } },
+ // Use opposite padding to move viewport center: only bottom right and center are visible.
+ { CameraOptions().withPitch(0).withPadding(EdgeInsets { 0, 0, viewSize.height * 0.5, viewSize.width * 0.5 }), { 3, 4 } },
+ // Use top padding to move viewport center: top row and center are visible.
+ { CameraOptions().withPitch(0).withPadding(EdgeInsets { viewSize.height * 0.5, 0, 0, 0 }), { 0, 1, 4 } },
+ // Use bottom padding: only bottom right and center are visible.
+ { CameraOptions().withPitch(0).withPadding(EdgeInsets { 0, 0, viewSize.height * 0.5, 0 }), { 2, 3, 4 } },
+ // Left padding and pitch: top left, bottom left and center are visible.
+ { CameraOptions().withPitch(45).withPadding(EdgeInsets { 0, viewSize.width * 0.5, 0, 0 }), { 0, 2, 4 } }
+ };
+
+ for (unsigned i = 0; i < expectedVisibleForCamera.size(); i++) {
+ auto testCase = expectedVisibleForCamera[i];
+ test.map.jumpTo(testCase.first);
+ test.frontend.render(test.map);
+ auto features = test.frontend.getRenderer()->queryRenderedFeatures(box, {});
+ for (uint64_t id : testCase.second) { // testCase.second is vector of ids expected.
+ EXPECT_NE(std::find_if(features.begin(), features.end(), [&id](auto feature) {
+ return id == feature.id.template get<uint64_t>();
+ }), features.end()) << "Point with id " << id << " is missing in test case " << i;
+ EXPECT_EQ(features.size(), testCase.second.size()) << " in test case " << i;
+ }
+ }
+}
+
TEST(Annotations, TopOffsetPixels) {
AnnotationTest test;
diff --git a/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png b/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png
new file mode 100644
index 0000000000..f161a95c8e
--- /dev/null
+++ b/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png
Binary files differ
diff --git a/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png b/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png
new file mode 100644
index 0000000000..edb8d07a40
--- /dev/null
+++ b/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png
Binary files differ
diff --git a/test/map/transform.test.cpp b/test/map/transform.test.cpp
index 33d325dfb2..3d37312b17 100644
--- a/test/map/transform.test.cpp
+++ b/test/map/transform.test.cpp
@@ -288,25 +288,48 @@ TEST(Transform, Padding) {
ASSERT_DOUBLE_EQ(0, transform.getLatLng().latitude());
ASSERT_DOUBLE_EQ(0, transform.getLatLng().longitude());
-
- transform.jumpTo(CameraOptions().withCenter(LatLng { 10, -100 }).withZoom(10.0));
+ CameraOptions nonPaddedCameraOptions = CameraOptions().withCenter(LatLng { 10, -100 }).withZoom(10.0);
+ transform.jumpTo(nonPaddedCameraOptions);
const LatLng trueCenter = transform.getLatLng();
ASSERT_DOUBLE_EQ(10, trueCenter.latitude());
ASSERT_DOUBLE_EQ(-100, trueCenter.longitude());
ASSERT_DOUBLE_EQ(10, transform.getZoom());
- const LatLng manualShiftedCenter = transform.getState().screenCoordinateToLatLng({
+ const LatLng screenCenter = transform.screenCoordinateToLatLng({
+ 1000.0 / 2.0,
1000.0 / 2.0,
- 1000.0 / 4.0,
+ });
+ const LatLng upperHalfCenter = transform.screenCoordinateToLatLng({
+ 1000.0 / 2.0,
+ 1000.0 * 0.25,
});
EdgeInsets padding(1000.0 / 2.0, 0, 0, 0);
- const LatLng shiftedCenter = transform.getLatLng(padding);
- ASSERT_NE(trueCenter.latitude(), shiftedCenter.latitude());
- ASSERT_NEAR(trueCenter.longitude(), shiftedCenter.longitude(), 1e-8);
- ASSERT_DOUBLE_EQ(manualShiftedCenter.latitude(), shiftedCenter.latitude());
- ASSERT_DOUBLE_EQ(manualShiftedCenter.longitude(), shiftedCenter.longitude());
+ // CameraOption center and zoom don't change when padding changes: center of
+ // viewport remains the same as padding defines viwport center offset in rendering.
+ CameraOptions paddedOptions = CameraOptions().withPadding(padding);
+ transform.jumpTo(paddedOptions);
+ const LatLng theSameCenter = transform.getLatLng();
+ ASSERT_DOUBLE_EQ(trueCenter.latitude(), theSameCenter.latitude());
+ ASSERT_DOUBLE_EQ(trueCenter.longitude(), theSameCenter.longitude());
+
+ // However, LatLng is now at the center of lower half - verify conversion
+ // from screen coordinate to LatLng.
+ const LatLng paddedLowerHalfScreenCenter = transform.screenCoordinateToLatLng({
+ 1000.0 / 2.0,
+ 1000.0 * 0.75,
+ });
+ ASSERT_NEAR(screenCenter.latitude(), paddedLowerHalfScreenCenter.latitude(), 1e-10);
+ ASSERT_NEAR(screenCenter.longitude(), paddedLowerHalfScreenCenter.longitude(), 1e-10);
+
+ // LatLng previously in upper half center, should now be under screen center.
+ const LatLng paddedScreenCenter = transform.screenCoordinateToLatLng({
+ 1000.0 / 2.0,
+ 1000.0 / 2.0,
+ });
+ ASSERT_NEAR(upperHalfCenter.latitude(), paddedScreenCenter.latitude(), 1e-10);
+ ASSERT_NEAR(upperHalfCenter.longitude(), paddedScreenCenter.longitude(), 1e-10);
}
TEST(Transform, MoveBy) {
@@ -440,7 +463,7 @@ TEST(Transform, Camera) {
flyOptions.transitionFrameFn = [&](double t) {
ASSERT_TRUE(t >= 0 && t <= 1);
ASSERT_LE(latLng2.latitude(), transform.getLatLng().latitude());
- ASSERT_GE(latLng2.longitude(), transform.getLatLng({}, LatLng::Unwrapped).longitude());
+ ASSERT_GE(latLng2.longitude(), transform.getLatLng(LatLng::Unwrapped).longitude());
};
flyOptions.transitionFinishFn = [&]() {
// XXX Fix precision loss in flyTo:
@@ -618,7 +641,7 @@ TEST(Transform, LatLngBounds) {
// Try crossing the antimeridian from the right.
transform.jumpTo(CameraOptions().withCenter(LatLng { 0.0, 200.0 }));
- ASSERT_DOUBLE_EQ(transform.getLatLng({}, LatLng::Unwrapped).longitude(), 180.0);
+ ASSERT_DOUBLE_EQ(transform.getLatLng(LatLng::Unwrapped).longitude(), 180.0);
ASSERT_DOUBLE_EQ(transform.getLatLng().longitude(), -180.0);
// -1 | 0 | +1
diff --git a/test/text/local_glyph_rasterizer.test.cpp b/test/text/local_glyph_rasterizer.test.cpp
index ec8a728e9f..86dc87b6c7 100644
--- a/test/text/local_glyph_rasterizer.test.cpp
+++ b/test/text/local_glyph_rasterizer.test.cpp
@@ -43,9 +43,9 @@ public:
MapAdapter map { frontend, MapObserver::nullObserver(), fileSource,
MapOptions().withMapMode(MapMode::Static).withSize(frontend.getSize())};
- void checkRendering(const char * name) {
+ void checkRendering(const char * name, double imageMatchPixelsThreshold = 0.05, double pixelMatchThreshold = 0.1) {
test::checkImage(std::string("test/fixtures/local_glyphs/") + name,
- frontend.render(map), 0.05, 0.1);
+ frontend.render(map), imageMatchPixelsThreshold, pixelMatchThreshold);
}
};
@@ -85,6 +85,50 @@ TEST(LocalGlyphRasterizer, NoLocal) {
return response;
};
test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json"));
- test.checkRendering("no_local");
+ test.checkRendering("no_local", 0.001, 0.1);
}
+TEST(LocalGlyphRasterizer, NoLocalWithContentInsets) {
+ // Expectations: content insets imply rendering to offsetted viewport center.
+ // Rendered text should be on the same offset and keep the same size as
+ // with no offset.
+ LocalGlyphRasterizerTest test({});
+
+ test.fileSource->glyphsResponse = [&] (const Resource& resource) {
+ EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
+ return response;
+ };
+ auto viewSize = test.frontend.getSize();
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json"));
+
+ // Expected image was created using center offset as in the line below - content insets,
+ // with no pitch defined, should produce the same output.
+ // test.map.moveBy({ viewSize.width * 0.25, 0 });
+
+ test.map.jumpTo(CameraOptions().withPadding(EdgeInsets { 0, viewSize.width * 0.25 * 2, 0, 0 }));
+ test.checkRendering("no_local_with_content_insets", 0.001, 0.1);
+}
+
+TEST(LocalGlyphRasterizer, NoLocalWithContentInsetsAndPitch) {
+ // Expectations: content insets imply rendering to offsetted viewport center.
+ // Rendered text should be on the same offset and keep the same size as
+ // with no offset.
+ LocalGlyphRasterizerTest test({});
+
+ test.fileSource->glyphsResponse = [&] (const Resource& resource) {
+ EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
+ return response;
+ };
+ auto viewSize = test.frontend.getSize();
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json"));
+
+ // Expected image was verified using no-padding render, offsetted to right
+ // using bitmap editor.
+
+ test.map.jumpTo(CameraOptions().withPitch(45).withPadding(EdgeInsets { 0, viewSize.width * 0.4, 0, 0 }));
+ test.checkRendering("no_local_with_content_insets_and_pitch", 0.001, 0.1);
+}