[core] Harden Transform anchor & padding usage
Use optional values for anchor & padding in Map and Transform functions instead of NaNs. Added unit tests to stress some edge cases.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#ifndef MBGL_MAP_MAP
|
||||
#define MBGL_MAP_MAP
|
||||
|
||||
#include <mbgl/util/optional.hpp>
|
||||
#include <mbgl/util/chrono.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/map/update.hpp>
|
||||
@@ -98,23 +99,23 @@ public:
|
||||
|
||||
// Position
|
||||
void moveBy(const ScreenCoordinate&, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, const ScreenCoordinate&, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, const EdgeInsets&, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, optional<ScreenCoordinate>, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, optional<EdgeInsets>, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, const Duration& = Duration::zero());
|
||||
LatLng getLatLng(const EdgeInsets& = {}) const;
|
||||
void resetPosition(const EdgeInsets& = {});
|
||||
LatLng getLatLng(optional<EdgeInsets> = {}) const;
|
||||
void resetPosition(optional<EdgeInsets> = {});
|
||||
|
||||
// Scale
|
||||
void scaleBy(double ds, const ScreenCoordinate& = { NAN, NAN }, const Duration& = Duration::zero());
|
||||
void setScale(double scale, const ScreenCoordinate& = { NAN, NAN }, const Duration& = Duration::zero());
|
||||
void scaleBy(double ds, optional<ScreenCoordinate> = {}, const Duration& = Duration::zero());
|
||||
void setScale(double scale, optional<ScreenCoordinate> = {}, const Duration& = Duration::zero());
|
||||
double getScale() const;
|
||||
void setZoom(double zoom, const Duration& = Duration::zero());
|
||||
void setZoom(double zoom, const EdgeInsets&, const Duration& = Duration::zero());
|
||||
void setZoom(double zoom, optional<EdgeInsets>, const Duration& = Duration::zero());
|
||||
double getZoom() const;
|
||||
void setLatLngZoom(const LatLng&, double zoom, const Duration& = Duration::zero());
|
||||
void setLatLngZoom(const LatLng&, double zoom, const EdgeInsets&, const Duration& = Duration::zero());
|
||||
CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&);
|
||||
CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&);
|
||||
void setLatLngZoom(const LatLng&, double zoom, optional<EdgeInsets>, const Duration& = Duration::zero());
|
||||
CameraOptions cameraForLatLngBounds(const LatLngBounds&, optional<EdgeInsets>);
|
||||
CameraOptions cameraForLatLngs(const std::vector<LatLng>&, optional<EdgeInsets>);
|
||||
void resetZoom();
|
||||
void setMinZoom(const double minZoom);
|
||||
double getMinZoom() const;
|
||||
@@ -124,15 +125,15 @@ public:
|
||||
// Rotation
|
||||
void rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& = Duration::zero());
|
||||
void setBearing(double degrees, const Duration& = Duration::zero());
|
||||
void setBearing(double degrees, const ScreenCoordinate&, const Duration& = Duration::zero());
|
||||
void setBearing(double degrees, const EdgeInsets&, const Duration& = Duration::zero());
|
||||
void setBearing(double degrees, optional<ScreenCoordinate>, const Duration& = Duration::zero());
|
||||
void setBearing(double degrees, optional<EdgeInsets>, const Duration& = Duration::zero());
|
||||
double getBearing() const;
|
||||
void resetNorth(const Duration& = Milliseconds(500));
|
||||
void resetNorth(const EdgeInsets&, const Duration& = Milliseconds(500));
|
||||
void resetNorth(optional<EdgeInsets>, const Duration& = Milliseconds(500));
|
||||
|
||||
// Pitch
|
||||
void setPitch(double pitch, const Duration& = Duration::zero());
|
||||
void setPitch(double pitch, const ScreenCoordinate&, const Duration& = Duration::zero());
|
||||
void setPitch(double pitch, optional<ScreenCoordinate>, const Duration& = Duration::zero());
|
||||
double getPitch() const;
|
||||
|
||||
// North Orientation
|
||||
|
||||
@@ -326,7 +326,7 @@ void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset)
|
||||
scale = 1.0 / scale;
|
||||
}
|
||||
|
||||
view->map->scaleBy(scale, { view->lastX, view->lastY });
|
||||
view->map->scaleBy(scale, mbgl::ScreenCoordinate { view->lastX, view->lastY });
|
||||
}
|
||||
|
||||
void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) {
|
||||
@@ -363,9 +363,9 @@ void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modi
|
||||
double now = glfwGetTime();
|
||||
if (now - view->lastClick < 0.4 /* ms */) {
|
||||
if (modifiers & GLFW_MOD_SHIFT) {
|
||||
view->map->scaleBy(0.5, { view->lastX, view->lastY }, mbgl::Milliseconds(500));
|
||||
view->map->scaleBy(0.5, mbgl::ScreenCoordinate { view->lastX, view->lastY }, mbgl::Milliseconds(500));
|
||||
} else {
|
||||
view->map->scaleBy(2.0, { view->lastX, view->lastY }, mbgl::Milliseconds(500));
|
||||
view->map->scaleBy(2.0, mbgl::ScreenCoordinate { view->lastX, view->lastY }, mbgl::Milliseconds(500));
|
||||
}
|
||||
}
|
||||
view->lastClick = now;
|
||||
|
||||
@@ -1146,7 +1146,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
|
||||
|
||||
if (log2(newScale) < _mbglMap->getMinZoom()) return;
|
||||
|
||||
_mbglMap->setScale(newScale, { centerPoint.x, centerPoint.y });
|
||||
_mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
|
||||
|
||||
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
|
||||
}
|
||||
@@ -1183,7 +1183,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
|
||||
|
||||
if (velocity)
|
||||
{
|
||||
_mbglMap->setScale(newScale, { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
|
||||
_mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
|
||||
}
|
||||
|
||||
[self notifyGestureDidEndWithDrift:velocity];
|
||||
@@ -1229,7 +1229,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
|
||||
newDegrees = fmaxf(newDegrees, -30);
|
||||
}
|
||||
|
||||
_mbglMap->setBearing(newDegrees, { centerPoint.x, centerPoint.y });
|
||||
_mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
|
||||
|
||||
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
|
||||
}
|
||||
@@ -1244,7 +1244,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
|
||||
CGFloat newRadians = radians + velocity * duration * 0.1;
|
||||
CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1;
|
||||
|
||||
_mbglMap->setBearing(newDegrees, { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
|
||||
_mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
|
||||
|
||||
[self notifyGestureDidEndWithDrift:YES];
|
||||
|
||||
@@ -1395,7 +1395,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
|
||||
centerPoint = self.userLocationAnnotationViewCenter;
|
||||
}
|
||||
_mbglMap->scaleBy(powf(2, newZoom) / _mbglMap->getScale(),
|
||||
{ centerPoint.x, centerPoint.y });
|
||||
mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
|
||||
|
||||
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
|
||||
}
|
||||
@@ -1430,7 +1430,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
|
||||
{
|
||||
centerPoint = self.userLocationAnnotationViewCenter;
|
||||
}
|
||||
_mbglMap->setPitch(pitchNew, centerPoint);
|
||||
_mbglMap->setPitch(pitchNew, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
|
||||
|
||||
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
|
||||
}
|
||||
@@ -1992,7 +1992,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
|
||||
else
|
||||
{
|
||||
CGPoint centerPoint = self.userLocationAnnotationViewCenter;
|
||||
_mbglMap->setBearing(direction, { centerPoint.x, centerPoint.y },
|
||||
_mbglMap->setBearing(direction, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y },
|
||||
MGLDurationInSeconds(duration));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,31 +172,29 @@ void Map::moveBy(const ScreenCoordinate& point, const Duration& duration) {
|
||||
}
|
||||
|
||||
void Map::setLatLng(const LatLng& latLng, const Duration& duration) {
|
||||
setLatLng(latLng, EdgeInsets(), duration);
|
||||
setLatLng(latLng, ScreenCoordinate {}, duration);
|
||||
}
|
||||
|
||||
void Map::setLatLng(const LatLng& latLng, const EdgeInsets& padding, const Duration& duration) {
|
||||
void Map::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
transform->setLatLng(latLng, padding, duration);
|
||||
update(Update::Repaint);
|
||||
}
|
||||
|
||||
void Map::setLatLng(const LatLng& latLng, const ScreenCoordinate& point, const Duration& duration) {
|
||||
transform->setLatLng(latLng, point, duration);
|
||||
void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
transform->setLatLng(latLng, anchor, duration);
|
||||
update(Update::Repaint);
|
||||
}
|
||||
|
||||
LatLng Map::getLatLng(const EdgeInsets& padding) const {
|
||||
LatLng Map::getLatLng(optional<EdgeInsets> padding) const {
|
||||
return transform->getLatLng(padding);
|
||||
}
|
||||
|
||||
void Map::resetPosition(const EdgeInsets& padding) {
|
||||
void Map::resetPosition(optional<EdgeInsets> padding) {
|
||||
CameraOptions camera;
|
||||
camera.angle = 0;
|
||||
camera.pitch = 0;
|
||||
camera.center = LatLng(0, 0);
|
||||
if (padding) {
|
||||
camera.padding = padding;
|
||||
}
|
||||
camera.padding = padding;
|
||||
camera.zoom = 0;
|
||||
transform->jumpTo(camera);
|
||||
update(Update::Zoom);
|
||||
@@ -205,13 +203,13 @@ void Map::resetPosition(const EdgeInsets& padding) {
|
||||
|
||||
#pragma mark - Scale
|
||||
|
||||
void Map::scaleBy(double ds, const ScreenCoordinate& point, const Duration& duration) {
|
||||
transform->scaleBy(ds, point, duration);
|
||||
void Map::scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
transform->scaleBy(ds, anchor, duration);
|
||||
update(Update::Zoom);
|
||||
}
|
||||
|
||||
void Map::setScale(double scale, const ScreenCoordinate& point, const Duration& duration) {
|
||||
transform->setScale(scale, point, duration);
|
||||
void Map::setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
transform->setScale(scale, anchor, duration);
|
||||
update(Update::Zoom);
|
||||
}
|
||||
|
||||
@@ -223,7 +221,7 @@ void Map::setZoom(double zoom, const Duration& duration) {
|
||||
setZoom(zoom, {}, duration);
|
||||
}
|
||||
|
||||
void Map::setZoom(double zoom, const EdgeInsets& padding, const Duration& duration) {
|
||||
void Map::setZoom(double zoom, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
transform->setZoom(zoom, padding, duration);
|
||||
update(Update::Zoom);
|
||||
}
|
||||
@@ -236,12 +234,12 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& durat
|
||||
setLatLngZoom(latLng, zoom, {}, duration);
|
||||
}
|
||||
|
||||
void Map::setLatLngZoom(const LatLng& latLng, double zoom, const EdgeInsets& padding, const Duration& duration) {
|
||||
void Map::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
transform->setLatLngZoom(latLng, zoom, padding, duration);
|
||||
update(Update::Zoom);
|
||||
}
|
||||
|
||||
CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding) {
|
||||
CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, optional<EdgeInsets> padding) {
|
||||
AnnotationSegment segment = {
|
||||
bounds.northwest(),
|
||||
bounds.southwest(),
|
||||
@@ -251,7 +249,7 @@ CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeI
|
||||
return cameraForLatLngs(segment, padding);
|
||||
}
|
||||
|
||||
CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding) {
|
||||
CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, optional<EdgeInsets> padding) {
|
||||
CameraOptions options;
|
||||
if (latLngs.empty()) {
|
||||
return options;
|
||||
@@ -272,26 +270,31 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
|
||||
double height = nePixel.y - swPixel.y;
|
||||
|
||||
// Calculate the zoom level.
|
||||
double scaleX = (getWidth() - padding.left - padding.right) / width;
|
||||
double scaleY = (getHeight() - padding.top - padding.bottom) / height;
|
||||
double scaleX = getWidth() / width;
|
||||
double scaleY = getHeight() / height;
|
||||
if (padding && *padding) {
|
||||
scaleX -= (padding->left + padding->right) / width;
|
||||
scaleY -= (padding->top + padding->bottom) / height;
|
||||
}
|
||||
double minScale = ::fmin(scaleX, scaleY);
|
||||
double zoom = ::log2(getScale() * minScale);
|
||||
zoom = util::clamp(zoom, getMinZoom(), getMaxZoom());
|
||||
|
||||
// Calculate the center point of a virtual bounds that is extended in all directions by padding.
|
||||
ScreenCoordinate paddedNEPixel = {
|
||||
nePixel.x + padding.right / minScale,
|
||||
nePixel.y + padding.top / minScale,
|
||||
};
|
||||
ScreenCoordinate paddedSWPixel = {
|
||||
swPixel.x - padding.left / minScale,
|
||||
swPixel.y - padding.bottom / minScale,
|
||||
};
|
||||
ScreenCoordinate centerPixel = {
|
||||
(paddedNEPixel.x + paddedSWPixel.x) / 2,
|
||||
(paddedNEPixel.y + paddedSWPixel.y) / 2,
|
||||
};
|
||||
|
||||
ScreenCoordinate centerPixel = nePixel + swPixel;
|
||||
if (padding && *padding) {
|
||||
ScreenCoordinate paddedNEPixel = {
|
||||
padding->right / minScale,
|
||||
padding->top / minScale,
|
||||
};
|
||||
ScreenCoordinate paddedSWPixel = {
|
||||
padding->left / minScale,
|
||||
padding->bottom / minScale,
|
||||
};
|
||||
centerPixel = centerPixel - paddedNEPixel - paddedSWPixel;
|
||||
}
|
||||
centerPixel /= 2;
|
||||
|
||||
// CameraOptions origin is at the top-left corner.
|
||||
centerPixel.y = viewportHeight - centerPixel.y;
|
||||
|
||||
@@ -349,12 +352,12 @@ void Map::setBearing(double degrees, const Duration& duration) {
|
||||
setBearing(degrees, EdgeInsets(), duration);
|
||||
}
|
||||
|
||||
void Map::setBearing(double degrees, const ScreenCoordinate& center, const Duration& duration) {
|
||||
transform->setAngle(-degrees * util::DEG2RAD, center, duration);
|
||||
void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
transform->setAngle(-degrees * util::DEG2RAD, anchor, duration);
|
||||
update(Update::Repaint);
|
||||
}
|
||||
|
||||
void Map::setBearing(double degrees, const EdgeInsets& padding, const Duration& duration) {
|
||||
void Map::setBearing(double degrees, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
transform->setAngle(-degrees * util::DEG2RAD, padding, duration);
|
||||
update(Update::Repaint);
|
||||
}
|
||||
@@ -372,10 +375,10 @@ void Map::resetNorth(const Duration& duration) {
|
||||
#pragma mark - Pitch
|
||||
|
||||
void Map::setPitch(double pitch, const Duration& duration) {
|
||||
setPitch(pitch, {NAN, NAN}, duration);
|
||||
setPitch(pitch, {}, duration);
|
||||
}
|
||||
|
||||
void Map::setPitch(double pitch, const ScreenCoordinate& anchor, const Duration& duration) {
|
||||
void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
transform->setPitch(pitch * util::DEG2RAD, anchor, duration);
|
||||
update(Update::Repaint);
|
||||
}
|
||||
|
||||
@@ -352,68 +352,57 @@ void Transform::moveBy(const ScreenCoordinate& offset, const Duration& duration)
|
||||
}
|
||||
|
||||
void Transform::setLatLng(const LatLng& latLng, const Duration& duration) {
|
||||
setLatLng(latLng, EdgeInsets(), duration);
|
||||
setLatLng(latLng, optional<ScreenCoordinate> {}, duration);
|
||||
}
|
||||
|
||||
void Transform::setLatLng(const LatLng& latLng, const EdgeInsets& padding, const Duration& duration) {
|
||||
if (!latLng) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Transform::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
if (!latLng) return;
|
||||
CameraOptions camera;
|
||||
camera.center = latLng;
|
||||
if (padding) {
|
||||
camera.padding = padding;
|
||||
}
|
||||
camera.padding = padding;
|
||||
easeTo(camera, duration);
|
||||
}
|
||||
|
||||
void Transform::setLatLng(const LatLng& latLng, const ScreenCoordinate& point, const Duration& duration) {
|
||||
if (!latLng || !point) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pretend the viewport is 0×0 around the passed-in point.
|
||||
void Transform::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
if (!latLng) return;
|
||||
CameraOptions camera;
|
||||
camera.center = latLng;
|
||||
EdgeInsets padding;
|
||||
padding.top = point.y;
|
||||
padding.left = point.x;
|
||||
padding.bottom = state.height - point.y;
|
||||
padding.right = state.width - point.x;
|
||||
if (padding) camera.padding = padding;
|
||||
if (anchor) {
|
||||
EdgeInsets padding;
|
||||
padding.top = anchor->y;
|
||||
padding.left = anchor->x;
|
||||
padding.bottom = state.height - anchor->y;
|
||||
padding.right = state.width - anchor->x;
|
||||
if (padding) camera.padding = padding;
|
||||
}
|
||||
easeTo(camera, duration);
|
||||
}
|
||||
|
||||
void Transform::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& duration) {
|
||||
setLatLngZoom(latLng, zoom, {}, duration);
|
||||
setLatLngZoom(latLng, zoom, EdgeInsets {}, duration);
|
||||
}
|
||||
|
||||
void Transform::setLatLngZoom(const LatLng& latLng, double zoom, const EdgeInsets& padding, const Duration& duration) {
|
||||
if (!latLng || std::isnan(zoom)) {
|
||||
return;
|
||||
}
|
||||
void Transform::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
if (!latLng || std::isnan(zoom)) return;
|
||||
|
||||
CameraOptions camera;
|
||||
camera.center = latLng;
|
||||
if (padding) {
|
||||
camera.padding = padding;
|
||||
}
|
||||
camera.padding = padding;
|
||||
camera.zoom = zoom;
|
||||
easeTo(camera, duration);
|
||||
}
|
||||
|
||||
LatLng Transform::getLatLng(const EdgeInsets& padding) const {
|
||||
if (padding) {
|
||||
return screenCoordinateToLatLng(padding.getCenter(state.width, state.height));
|
||||
LatLng Transform::getLatLng(optional<EdgeInsets> padding) const {
|
||||
if (padding && *padding) {
|
||||
return screenCoordinateToLatLng(padding->getCenter(state.width, state.height));
|
||||
} else {
|
||||
return state.getLatLng();
|
||||
}
|
||||
}
|
||||
|
||||
ScreenCoordinate Transform::getScreenCoordinate(const EdgeInsets& padding) const {
|
||||
if (padding) {
|
||||
return padding.getCenter(state.width, state.height);
|
||||
ScreenCoordinate Transform::getScreenCoordinate(optional<EdgeInsets> padding) const {
|
||||
if (padding && *padding) {
|
||||
return padding->getCenter(state.width, state.height);
|
||||
} else {
|
||||
return { state.width / 2., state.height / 2. };
|
||||
}
|
||||
@@ -422,20 +411,24 @@ ScreenCoordinate Transform::getScreenCoordinate(const EdgeInsets& padding) const
|
||||
|
||||
#pragma mark - Zoom
|
||||
|
||||
void Transform::scaleBy(double ds, const ScreenCoordinate& center, const Duration& duration) {
|
||||
if (std::isnan(ds) || !center) {
|
||||
return;
|
||||
}
|
||||
|
||||
double scale = util::clamp(state.scale * ds, state.min_scale, state.max_scale);
|
||||
setScale(scale, center, duration);
|
||||
void Transform::scaleBy(double ds, const Duration& duration) {
|
||||
scaleBy(ds, optional<ScreenCoordinate> {}, duration);
|
||||
}
|
||||
|
||||
void Transform::setZoom(double zoom, const ScreenCoordinate& anchor, const Duration& duration) {
|
||||
void Transform::scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
double scale = util::clamp(state.scale * ds, state.min_scale, state.max_scale);
|
||||
setScale(scale, anchor, duration);
|
||||
}
|
||||
|
||||
void Transform::setZoom(double zoom, const Duration& duration) {
|
||||
setZoom(zoom, optional<ScreenCoordinate> {}, duration);
|
||||
}
|
||||
|
||||
void Transform::setZoom(double zoom, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
setScale(state.zoomScale(zoom), anchor, duration);
|
||||
}
|
||||
|
||||
void Transform::setZoom(double zoom, const EdgeInsets& padding, const Duration& duration) {
|
||||
void Transform::setZoom(double zoom, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
setScale(state.zoomScale(zoom), padding, duration);
|
||||
}
|
||||
|
||||
@@ -447,19 +440,22 @@ double Transform::getScale() const {
|
||||
return state.scale;
|
||||
}
|
||||
|
||||
void Transform::setScale(double scale, const ScreenCoordinate& anchor, const Duration& duration) {
|
||||
if (std::isnan(scale)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Transform::setScale(double scale, const Duration& duration) {
|
||||
setScale(scale, optional<ScreenCoordinate> {}, duration);
|
||||
}
|
||||
|
||||
void Transform::setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
if (std::isnan(scale)) return;
|
||||
CameraOptions camera;
|
||||
camera.zoom = state.scaleZoom(scale);
|
||||
if (anchor) camera.anchor = anchor;
|
||||
camera.anchor = anchor;
|
||||
easeTo(camera, duration);
|
||||
}
|
||||
|
||||
void Transform::setScale(double scale, const EdgeInsets& padding, const Duration& duration) {
|
||||
setScale(scale, getScreenCoordinate(padding), duration);
|
||||
void Transform::setScale(double scale, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
optional<ScreenCoordinate> anchor;
|
||||
if (padding) anchor = getScreenCoordinate(padding);
|
||||
setScale(scale, anchor, duration);
|
||||
}
|
||||
|
||||
void Transform::setMinZoom(const double minZoom) {
|
||||
@@ -502,22 +498,21 @@ void Transform::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate&
|
||||
}
|
||||
|
||||
void Transform::setAngle(double angle, const Duration& duration) {
|
||||
setAngle(angle, ScreenCoordinate { NAN, NAN }, duration);
|
||||
setAngle(angle, optional<ScreenCoordinate> {}, duration);
|
||||
}
|
||||
|
||||
void Transform::setAngle(double angle, const ScreenCoordinate& anchor, const Duration& duration) {
|
||||
if (std::isnan(angle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Transform::setAngle(double angle, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
if (std::isnan(angle)) return;
|
||||
CameraOptions camera;
|
||||
camera.angle = angle;
|
||||
if (anchor) camera.anchor = anchor;
|
||||
camera.anchor = anchor;
|
||||
easeTo(camera, duration);
|
||||
}
|
||||
|
||||
void Transform::setAngle(double angle, const EdgeInsets& padding, const Duration& duration) {
|
||||
setAngle(angle, getScreenCoordinate(padding), duration);
|
||||
void Transform::setAngle(double angle, optional<EdgeInsets> padding, const Duration& duration) {
|
||||
optional<ScreenCoordinate> anchor;
|
||||
if (padding && *padding) anchor = getScreenCoordinate(padding);
|
||||
setAngle(angle, anchor, duration);
|
||||
}
|
||||
|
||||
double Transform::getAngle() const {
|
||||
@@ -527,17 +522,14 @@ double Transform::getAngle() const {
|
||||
#pragma mark - Pitch
|
||||
|
||||
void Transform::setPitch(double pitch, const Duration& duration) {
|
||||
setPitch(pitch, ScreenCoordinate {}, duration);
|
||||
setPitch(pitch, optional<ScreenCoordinate> {}, duration);
|
||||
}
|
||||
|
||||
void Transform::setPitch(double pitch, const ScreenCoordinate& anchor, const Duration& duration) {
|
||||
if (std::isnan(pitch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Transform::setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& duration) {
|
||||
if (std::isnan(pitch)) return;
|
||||
CameraOptions camera;
|
||||
camera.pitch = pitch;
|
||||
if (anchor) camera.anchor = anchor;
|
||||
camera.anchor = anchor;
|
||||
easeTo(camera, duration);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <mbgl/util/chrono.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
@@ -43,38 +44,47 @@ public:
|
||||
top to bottom and from left to right. */
|
||||
void moveBy(const ScreenCoordinate& offset, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, const EdgeInsets&, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, const ScreenCoordinate&, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, optional<EdgeInsets>, const Duration& = Duration::zero());
|
||||
void setLatLng(const LatLng&, optional<ScreenCoordinate>, const Duration& = Duration::zero());
|
||||
void setLatLngZoom(const LatLng&, double zoom, const Duration& = Duration::zero());
|
||||
void setLatLngZoom(const LatLng&, double zoom, const EdgeInsets&, const Duration& = Duration::zero());
|
||||
LatLng getLatLng(const EdgeInsets& = {}) const;
|
||||
ScreenCoordinate getScreenCoordinate(const EdgeInsets& = {}) const;
|
||||
void setLatLngZoom(const LatLng&, double zoom, optional<EdgeInsets>, const Duration& = Duration::zero());
|
||||
LatLng getLatLng(optional<EdgeInsets> = {}) const;
|
||||
ScreenCoordinate getScreenCoordinate(optional<EdgeInsets> = {}) const;
|
||||
|
||||
// Zoom
|
||||
|
||||
/** Scales the map, keeping the given point fixed within the view.
|
||||
@param ds The difference in scale factors to scale the map by. */
|
||||
void scaleBy(double ds, const Duration& = Duration::zero());
|
||||
/** Scales the map, keeping the given point fixed within the view.
|
||||
@param ds The difference in scale factors to scale the map by.
|
||||
@param anchor A point relative to the top-left corner of the view.
|
||||
If unspecified, the center point is fixed within the view. */
|
||||
void scaleBy(double ds, const ScreenCoordinate& anchor = {}, const Duration& = Duration::zero());
|
||||
void scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero());
|
||||
/** Sets the scale factor, keeping the given point fixed within the view.
|
||||
@param scale The new scale factor. */
|
||||
void setScale(double scale, const Duration& = Duration::zero());
|
||||
/** Sets the scale factor, keeping the given point fixed within the view.
|
||||
@param scale The new scale factor.
|
||||
@param anchor A point relative to the top-left corner of the view.
|
||||
If unspecified, the center point is fixed within the view. */
|
||||
void setScale(double scale, const ScreenCoordinate& anchor = {}, const Duration& = Duration::zero());
|
||||
void setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero());
|
||||
/** Sets the scale factor, keeping the center point fixed within the inset view.
|
||||
@param scale The new scale factor.
|
||||
@param padding The viewport padding that affects the fixed center point. */
|
||||
void setScale(double scale, const EdgeInsets& padding, const Duration& = Duration::zero());
|
||||
void setScale(double scale, optional<EdgeInsets> padding, const Duration& = Duration::zero());
|
||||
/** Sets the zoom level, keeping the given point fixed within the view.
|
||||
@param zoom The new zoom level. */
|
||||
void setZoom(double zoom, const Duration& = Duration::zero());
|
||||
/** Sets the zoom level, keeping the given point fixed within the view.
|
||||
@param zoom The new zoom level.
|
||||
@param anchor A point relative to the top-left corner of the view.
|
||||
If unspecified, the center point is fixed within the view. */
|
||||
void setZoom(double zoom, const ScreenCoordinate& anchor = {}, const Duration& = Duration::zero());
|
||||
void setZoom(double zoom, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero());
|
||||
/** Sets the zoom level, keeping the center point fixed within the inset view.
|
||||
@param zoom The new zoom level.
|
||||
@param padding The viewport padding that affects the fixed center point. */
|
||||
void setZoom(double zoom, const EdgeInsets& padding, const Duration& = Duration::zero());
|
||||
void setZoom(double zoom, optional<EdgeInsets> padding, const Duration& = Duration::zero());
|
||||
/** Returns the zoom level. */
|
||||
double getZoom() const;
|
||||
/** Returns the scale factor. */
|
||||
@@ -94,12 +104,12 @@ public:
|
||||
@param angle The new angle of rotation, measured in radians
|
||||
counterclockwise from true north.
|
||||
@param anchor A point relative to the top-left corner of the view. */
|
||||
void setAngle(double angle, const ScreenCoordinate& anchor, const Duration& = Duration::zero());
|
||||
void setAngle(double angle, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero());
|
||||
/** Sets the angle of rotation, keeping the center point fixed within the inset view.
|
||||
@param angle The new angle of rotation, measured in radians
|
||||
counterclockwise from true north.
|
||||
@param padding The viewport padding that affects the fixed center point. */
|
||||
void setAngle(double angle, const EdgeInsets& padding, const Duration& = Duration::zero());
|
||||
void setAngle(double angle, optional<EdgeInsets> padding, const Duration& = Duration::zero());
|
||||
/** Returns the angle of rotation.
|
||||
@return The angle of rotation, measured in radians counterclockwise from
|
||||
true north. */
|
||||
@@ -114,7 +124,7 @@ public:
|
||||
@param angle The new pitch angle, measured in radians toward the
|
||||
horizon.
|
||||
@param anchor A point relative to the top-left corner of the view. */
|
||||
void setPitch(double pitch, const ScreenCoordinate& anchor, const Duration& = Duration::zero());
|
||||
void setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero());
|
||||
double getPitch() const;
|
||||
|
||||
// North Orientation
|
||||
|
||||
@@ -222,17 +222,90 @@ TEST(Transform, Anchor) {
|
||||
ASSERT_DOUBLE_EQ(10, transform.getZoom());
|
||||
ASSERT_DOUBLE_EQ(0, transform.getAngle());
|
||||
|
||||
const ScreenCoordinate invalidAnchorPoint = ScreenCoordinate::null();
|
||||
const ScreenCoordinate anchorPoint = { 150, 150 };
|
||||
|
||||
const LatLng anchorLatLng = transform.getState().screenCoordinateToLatLng(anchorPoint);
|
||||
ASSERT_NE(latLng.latitude, anchorLatLng.latitude);
|
||||
ASSERT_NE(latLng.longitude, anchorLatLng.longitude);
|
||||
|
||||
transform.setLatLngZoom(latLng, 2);
|
||||
transform.scaleBy(1);
|
||||
ASSERT_DOUBLE_EQ(4, transform.getScale());
|
||||
ASSERT_DOUBLE_EQ(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_DOUBLE_EQ(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
transform.scaleBy(1.5, invalidAnchorPoint);
|
||||
ASSERT_DOUBLE_EQ(6, transform.getScale());
|
||||
ASSERT_DOUBLE_EQ(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_DOUBLE_EQ(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
transform.scaleBy(2, anchorPoint);
|
||||
ASSERT_DOUBLE_EQ(12, transform.getScale());
|
||||
ASSERT_NE(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_NE(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
transform.setLatLngZoom(latLng, 10);
|
||||
transform.setScale(2 << 2);
|
||||
ASSERT_DOUBLE_EQ(2 << 2, transform.getScale());
|
||||
ASSERT_NEAR(latLng.latitude, transform.getLatLng().latitude, 0.000001);
|
||||
ASSERT_NEAR(latLng.longitude, transform.getLatLng().longitude, 0.000001);
|
||||
|
||||
transform.setScale(2 << 4, invalidAnchorPoint);
|
||||
ASSERT_DOUBLE_EQ(2 << 4, transform.getScale());
|
||||
ASSERT_NEAR(latLng.latitude, transform.getLatLng().latitude, 0.000001);
|
||||
ASSERT_NEAR(latLng.longitude, transform.getLatLng().longitude, 0.000001);
|
||||
|
||||
transform.setScale(2 << 6, anchorPoint);
|
||||
ASSERT_DOUBLE_EQ(2 << 6, transform.getScale());
|
||||
ASSERT_NE(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_NE(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
transform.setLatLngZoom(latLng, 10);
|
||||
transform.setZoom(2);
|
||||
ASSERT_DOUBLE_EQ(2, transform.getZoom());
|
||||
ASSERT_NEAR(latLng.latitude, transform.getLatLng().latitude, 0.000001);
|
||||
ASSERT_NEAR(latLng.longitude, transform.getLatLng().longitude, 0.000001);
|
||||
|
||||
transform.setZoom(4, invalidAnchorPoint);
|
||||
ASSERT_DOUBLE_EQ(4, transform.getZoom());
|
||||
ASSERT_NEAR(latLng.latitude, transform.getLatLng().latitude, 0.000001);
|
||||
ASSERT_NEAR(latLng.longitude, transform.getLatLng().longitude, 0.000001);
|
||||
|
||||
transform.setZoom(8, anchorPoint);
|
||||
ASSERT_DOUBLE_EQ(8, transform.getZoom());
|
||||
ASSERT_NE(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_NE(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
transform.setLatLngZoom(latLng, 10);
|
||||
transform.setAngle(M_PI_4);
|
||||
ASSERT_NEAR(M_PI_4, transform.getAngle(), 0.000001);
|
||||
ASSERT_DOUBLE_EQ(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_DOUBLE_EQ(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
const ScreenCoordinate anchorPoint = { 150, 150 };
|
||||
const LatLng anchorLatLng = transform.getState().screenCoordinateToLatLng(anchorPoint);
|
||||
transform.setAngle(0, invalidAnchorPoint);
|
||||
ASSERT_DOUBLE_EQ(0, transform.getAngle());
|
||||
ASSERT_DOUBLE_EQ(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_DOUBLE_EQ(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
transform.setAngle(-45 * util::DEG2RAD, anchorPoint);
|
||||
ASSERT_NEAR(-45 / util::RAD2DEG, transform.getAngle(), 0.000001);
|
||||
ASSERT_NE(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_NE(latLng.longitude, transform.getLatLng().longitude);
|
||||
ASSERT_NEAR(anchorLatLng.latitude, transform.getLatLng().latitude, 1);
|
||||
ASSERT_NEAR(anchorLatLng.longitude, transform.getLatLng().longitude, 1);
|
||||
|
||||
transform.setLatLngZoom(latLng, 10);
|
||||
transform.setPitch(10 * util::DEG2RAD);
|
||||
ASSERT_DOUBLE_EQ(10 / util::RAD2DEG, transform.getPitch());
|
||||
ASSERT_DOUBLE_EQ(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_DOUBLE_EQ(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
transform.setPitch(15 * util::DEG2RAD, invalidAnchorPoint);
|
||||
ASSERT_DOUBLE_EQ(15 / util::RAD2DEG, transform.getPitch());
|
||||
ASSERT_DOUBLE_EQ(latLng.latitude, transform.getLatLng().latitude);
|
||||
ASSERT_DOUBLE_EQ(latLng.longitude, transform.getLatLng().longitude);
|
||||
|
||||
transform.setPitch(20 * util::DEG2RAD, anchorPoint);
|
||||
ASSERT_DOUBLE_EQ(20 / util::RAD2DEG, transform.getPitch());
|
||||
ASSERT_NEAR(anchorLatLng.latitude, transform.getLatLng().latitude, 1);
|
||||
ASSERT_NEAR(anchorLatLng.longitude, transform.getLatLng().longitude, 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user