diff options
Diffstat (limited to 'src/mbgl/map/transform.cpp')
-rw-r--r-- | src/mbgl/map/transform.cpp | 274 |
1 files changed, 148 insertions, 126 deletions
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 6b44e633d9..9245d7c5bc 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -49,14 +49,17 @@ void Transform::resize(const Size size) { throw std::runtime_error("failed to resize: size is empty"); } - if (state.size == size) { + if (state.getSize() == size) { return; } observer.onCameraWillChange(MapObserver::CameraChangeMode::Immediate); - - state.size = size; - state.constrain(state.scale, state.x, state.y); + state.setSize(size); + double scale{state.getScale()}; + double x{state.getX()}; + double y{state.getY()}; + state.constrain(scale, x, y); + state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y)); observer.onCameraDidChange(MapObserver::CameraChangeMode::Immediate); } @@ -83,14 +86,14 @@ void Transform::jumpTo(const CameraOptions& camera) { */ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& animation) { Duration duration = animation.duration.value_or(Duration::zero()); - if (state.bounds == LatLngBounds() && !isGestureInProgress() && duration != Duration::zero()) { + if (state.getLatLngBounds() == LatLngBounds() && !isGestureInProgress() && duration != Duration::zero()) { // reuse flyTo, without exaggerated animation, to achieve constant ground speed. return flyTo(camera, animation, true); } - const EdgeInsets& padding = camera.padding.value_or(state.edgeInsets); + const EdgeInsets& padding = camera.padding.value_or(state.getEdgeInsets()); LatLng startLatLng = getLatLng(LatLng::Unwrapped); const LatLng& unwrappedLatLng = camera.center.value_or(startLatLng); - const LatLng& latLng = state.bounds != LatLngBounds() ? unwrappedLatLng : unwrappedLatLng.wrapped(); + const LatLng& latLng = state.getLatLngBounds() != LatLngBounds() ? unwrappedLatLng : unwrappedLatLng.wrapped(); double zoom = camera.zoom.value_or(getZoom()); double bearing = camera.bearing ? -*camera.bearing * util::DEG2RAD : getBearing(); double pitch = camera.pitch ? *camera.pitch * util::DEG2RAD : getPitch(); @@ -102,7 +105,7 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim return; } - if (state.bounds == LatLngBounds()) { + if (state.getLatLngBounds() == LatLngBounds()) { if (isGestureInProgress()) { // If gesture in progress, we transfer the wrap rounds from the end longitude into // start, so the "scroll effect" of rounding the world is the same while assuring the @@ -115,48 +118,51 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim } } - const Point<double> startPoint = Projection::project(startLatLng, state.scale); - const Point<double> endPoint = Projection::project(latLng, state.scale); + const Point<double> startPoint = Projection::project(startLatLng, state.getScale()); + const Point<double> endPoint = Projection::project(latLng, state.getScale()); // Constrain camera options. zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom()); pitch = util::clamp(pitch, util::PITCH_MIN, util::PITCH_MAX); // Minimize rotation by taking the shorter path around the circle. - bearing = _normalizeAngle(bearing, state.bearing); - state.bearing = _normalizeAngle(state.bearing, bearing); + bearing = _normalizeAngle(bearing, state.getBearing()); + state.setBearing(_normalizeAngle(state.getBearing(), bearing)); const double startZoom = state.getZoom(); - const double startBearing = state.bearing; - const double startPitch = state.pitch; - state.panning = unwrappedLatLng != startLatLng; - state.scaling = zoom != startZoom; - state.rotating = bearing != startBearing; - const EdgeInsets startEdgeInsets = state.edgeInsets; - - startTransition(camera, animation, [=](double t) { - Point<double> framePoint = util::interpolate(startPoint, endPoint, t); - LatLng frameLatLng = Projection::unproject(framePoint, state.zoomScale(startZoom)); - double frameZoom = util::interpolate(startZoom, zoom, t); - state.setLatLngZoom(frameLatLng, frameZoom); - - if (bearing != startBearing) { - state.bearing = util::wrap(util::interpolate(startBearing, bearing, t), -M_PI, M_PI); - } - if (padding != startEdgeInsets) { - // Interpolate edge insets - state.edgeInsets = { - util::interpolate(startEdgeInsets.top(), padding.top(), t), - util::interpolate(startEdgeInsets.left(), padding.left(), t), - util::interpolate(startEdgeInsets.bottom(), padding.bottom(), t), - util::interpolate(startEdgeInsets.right(), padding.right(), t) - }; - } - auto maxPitch = getMaxPitchForEdgeInsets(state.edgeInsets); - if (pitch != startPitch || maxPitch < startPitch) { - state.pitch = std::min(maxPitch, util::interpolate(startPitch, pitch, t)); - } - }, duration); + const double startBearing = state.getBearing(); + const double startPitch = state.getPitch(); + state.setProperties(TransformStateProperties() + .withPanningInProgress(unwrappedLatLng != startLatLng) + .withScalingInProgress(zoom != startZoom) + .withRotatingInProgress(bearing != startBearing)); + const EdgeInsets startEdgeInsets = state.getEdgeInsets(); + + startTransition( + camera, + animation, + [=](double t) { + Point<double> framePoint = util::interpolate(startPoint, endPoint, t); + LatLng frameLatLng = Projection::unproject(framePoint, state.zoomScale(startZoom)); + double frameZoom = util::interpolate(startZoom, zoom, t); + state.setLatLngZoom(frameLatLng, frameZoom); + if (bearing != startBearing) { + state.setBearing(util::wrap(util::interpolate(startBearing, bearing, t), -M_PI, M_PI)); + } + if (padding != startEdgeInsets) { + // Interpolate edge insets + EdgeInsets edgeInsets; + state.setEdgeInsets({util::interpolate(startEdgeInsets.top(), padding.top(), t), + util::interpolate(startEdgeInsets.left(), padding.left(), t), + util::interpolate(startEdgeInsets.bottom(), padding.bottom(), t), + util::interpolate(startEdgeInsets.right(), padding.right(), t)}); + } + double maxPitch = getMaxPitchForEdgeInsets(state.getEdgeInsets()); + if (pitch != startPitch || maxPitch < startPitch) { + state.setPitch(std::min(maxPitch, util::interpolate(startPitch, pitch, t))); + } + }, + duration); } /** This method implements an “optimal path” animation, as detailed in: @@ -167,14 +173,14 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim Where applicable, local variable documentation begins with the associated variable or function in van Wijk (2003). */ -void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &animation, bool linearZoomInterpolation) { - const EdgeInsets& padding = camera.padding.value_or(state.edgeInsets); +void Transform::flyTo(const CameraOptions& camera, const AnimationOptions& animation, bool linearZoomInterpolation) { + const EdgeInsets& padding = camera.padding.value_or(state.getEdgeInsets()); const LatLng& latLng = camera.center.value_or(getLatLng(LatLng::Unwrapped)).wrapped(); double zoom = camera.zoom.value_or(getZoom()); double bearing = camera.bearing ? -*camera.bearing * util::DEG2RAD : getBearing(); double pitch = camera.pitch ? *camera.pitch * util::DEG2RAD : getPitch(); - if (std::isnan(zoom) || std::isnan(bearing) || std::isnan(pitch) || state.size.isEmpty()) { + if (std::isnan(zoom) || std::isnan(bearing) || std::isnan(pitch) || state.getSize().isEmpty()) { if (animation.transitionFinishFn) { animation.transitionFinishFn(); } @@ -185,25 +191,25 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima LatLng startLatLng = getLatLng(LatLng::Unwrapped).wrapped(); startLatLng.unwrapForShortestPath(latLng); - const Point<double> startPoint = Projection::project(startLatLng, state.scale); - const Point<double> endPoint = Projection::project(latLng, state.scale); + const Point<double> startPoint = Projection::project(startLatLng, state.getScale()); + const Point<double> endPoint = Projection::project(latLng, state.getScale()); // Constrain camera options. zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom()); pitch = util::clamp(pitch, util::PITCH_MIN, util::PITCH_MAX); // Minimize rotation by taking the shorter path around the circle. - bearing = _normalizeAngle(bearing, state.bearing); - state.bearing = _normalizeAngle(state.bearing, bearing); - - const double startZoom = state.scaleZoom(state.scale); - const double startBearing = state.bearing; - const double startPitch = state.pitch; + bearing = _normalizeAngle(bearing, state.getBearing()); + state.setBearing(_normalizeAngle(state.getBearing(), bearing)); + const double startZoom = state.scaleZoom(state.getScale()); + const double startBearing = state.getBearing(); + const double startPitch = state.getPitch(); /// w₀: Initial visible span, measured in pixels at the initial scale. /// Known henceforth as a <i>screenful</i>. - double w0 = std::max(state.size.width - padding.left() - padding.right(), - state.size.height - padding.top() - padding.bottom()); + + double w0 = std::max(state.getSize().width - padding.left() - padding.right(), + state.getSize().height - padding.top() - padding.bottom()); /// w₁: Final visible span, measured in pixels with respect to the initial /// scale. double w1 = w0 / state.zoomScale(zoom - startZoom); @@ -286,56 +292,60 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima return; } - const double startScale = state.scale; - state.panning = true; - state.scaling = true; - state.rotating = bearing != startBearing; - const EdgeInsets startEdgeInsets = state.edgeInsets; - - startTransition(camera, animation, [=](double k) { - /// s: The distance traveled along the flight path, measured in - /// ρ-screenfuls. - double s = k * S; - double us = k == 1.0 ? 1.0 : u(s); - - // Calculate the current point and zoom level along the flight path. - Point<double> framePoint = util::interpolate(startPoint, endPoint, us); - double frameZoom = linearZoomInterpolation ? util::interpolate(startZoom, zoom, k) - : startZoom + state.scaleZoom(1 / w(s)); - - // Zoom can be NaN if size is empty. - if (std::isnan(frameZoom)) { - frameZoom = zoom; - } + const double startScale = state.getScale(); + state.setProperties( + TransformStateProperties().withPanningInProgress(true).withScalingInProgress(true).withRotatingInProgress( + bearing != startBearing)); + const EdgeInsets startEdgeInsets = state.getEdgeInsets(); + + startTransition( + camera, + animation, + [=](double k) { + /// s: The distance traveled along the flight path, measured in + /// ρ-screenfuls. + double s = k * S; + double us = k == 1.0 ? 1.0 : u(s); + + // Calculate the current point and zoom level along the flight path. + Point<double> framePoint = util::interpolate(startPoint, endPoint, us); + double frameZoom = + linearZoomInterpolation ? util::interpolate(startZoom, zoom, k) : startZoom + state.scaleZoom(1 / w(s)); + + // Zoom can be NaN if size is empty. + if (std::isnan(frameZoom)) { + frameZoom = zoom; + } - // Convert to geographic coordinates and set the new viewpoint. - LatLng frameLatLng = Projection::unproject(framePoint, startScale); - state.setLatLngZoom(frameLatLng, frameZoom); + // Convert to geographic coordinates and set the new viewpoint. + LatLng frameLatLng = Projection::unproject(framePoint, startScale); + state.setLatLngZoom(frameLatLng, frameZoom); + if (bearing != startBearing) { + state.setBearing(util::wrap(util::interpolate(startBearing, bearing, k), -M_PI, M_PI)); + } - if (bearing != startBearing) { - state.bearing = util::wrap(util::interpolate(startBearing, bearing, k), -M_PI, M_PI); - } - if (padding != startEdgeInsets) { - // Interpolate edge insets - state.edgeInsets = { - util::interpolate(startEdgeInsets.top(), padding.top(), k), - util::interpolate(startEdgeInsets.left(), padding.left(), k), - util::interpolate(startEdgeInsets.bottom(), padding.bottom(), k), - util::interpolate(startEdgeInsets.right(), padding.right(), k) - }; - } - auto maxPitch = getMaxPitchForEdgeInsets(state.edgeInsets); - if (pitch != startPitch || maxPitch < startPitch) { - state.pitch = std::min(maxPitch, util::interpolate(startPitch, pitch, k)); - } - }, duration); + if (padding != startEdgeInsets) { + // Interpolate edge insets + state.setEdgeInsets({util::interpolate(startEdgeInsets.top(), padding.top(), k), + util::interpolate(startEdgeInsets.left(), padding.left(), k), + util::interpolate(startEdgeInsets.bottom(), padding.bottom(), k), + util::interpolate(startEdgeInsets.right(), padding.right(), k)}); + } + double maxPitch = getMaxPitchForEdgeInsets(state.getEdgeInsets()); + + if (pitch != startPitch || maxPitch < startPitch) { + state.setPitch(std::min(maxPitch, util::interpolate(startPitch, pitch, k))); + } + }, + duration); } #pragma mark - Position void Transform::moveBy(const ScreenCoordinate& offset, const AnimationOptions& animation) { - ScreenCoordinate centerOffset = { offset.x, offset.y }; - ScreenCoordinate pointOnScreen = state.edgeInsets.getCenter(state.size.width, state.size.height) - centerOffset; + ScreenCoordinate centerOffset = {offset.x, offset.y}; + ScreenCoordinate pointOnScreen = + state.getEdgeInsets().getCenter(state.getSize().width, state.getSize().height) - centerOffset; // Use unwrapped LatLng to carry information about moveBy direction. easeTo(CameraOptions().withCenter(screenCoordinateToLatLng(pointOnScreen, LatLng::Unwrapped)), animation); } @@ -371,8 +381,10 @@ void Transform::setMaxZoom(const double maxZoom) { #pragma mark - Bearing -void Transform::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& animation) { - ScreenCoordinate center = state.edgeInsets.getCenter(state.size.width, state.size.height); +void Transform::rotateBy(const ScreenCoordinate& first, + const ScreenCoordinate& second, + const AnimationOptions& animation) { + ScreenCoordinate center = state.getEdgeInsets().getCenter(state.getSize().width, state.getSize().height); const ScreenCoordinate offset = first - center; const double distance = std::sqrt(std::pow(2, offset.x) + std::pow(2, offset.y)); @@ -385,25 +397,29 @@ void Transform::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& center.y = first.y + std::sin(rotateBearing) * heightOffset; } - const double bearing = -(state.bearing + util::angle_between(first - center, second - center)) * util::RAD2DEG; + const double bearing = -(state.getBearing() + util::angle_between(first - center, second - center)) * util::RAD2DEG; easeTo(CameraOptions().withBearing(bearing), animation); } double Transform::getBearing() const { - return state.bearing; + return state.getBearing(); } #pragma mark - Pitch double Transform::getPitch() const { - return state.pitch; + return state.getPitch(); } #pragma mark - North Orientation void Transform::setNorthOrientation(NorthOrientation orientation) { - state.orientation = orientation; - state.constrain(state.scale, state.x, state.y); + state.setNorthOrientation(orientation); + double scale{state.getScale()}; + double x{state.getX()}; + double y{state.getY()}; + state.constrain(scale, x, y); + state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y)); } NorthOrientation Transform::getNorthOrientation() const { @@ -413,8 +429,12 @@ NorthOrientation Transform::getNorthOrientation() const { #pragma mark - Constrain mode void Transform::setConstrainMode(mbgl::ConstrainMode mode) { - state.constrainMode = mode; - state.constrain(state.scale, state.x, state.y); + state.setConstrainMode(mode); + double scale{state.getScale()}; + double x{state.getX()}; + double y{state.getY()}; + state.constrain(scale, x, y); + state.setProperties(TransformStateProperties().withScale(scale).withX(x).withY(y)); } ConstrainMode Transform::getConstrainMode() const { @@ -424,7 +444,7 @@ ConstrainMode Transform::getConstrainMode() const { #pragma mark - Viewport mode void Transform::setViewportMode(mbgl::ViewportMode mode) { - state.viewportMode = mode; + state.setViewportMode(mode); } ViewportMode Transform::getViewportMode() const { @@ -434,16 +454,17 @@ ViewportMode Transform::getViewportMode() const { #pragma mark - Projection mode void Transform::setProjectionMode(const ProjectionMode& options) { - state.axonometric = options.axonometric.value_or(state.axonometric); - state.xSkew = options.xSkew.value_or(state.xSkew); - state.ySkew = options.ySkew.value_or(state.ySkew); + state.setProperties(TransformStateProperties() + .withAxonometric(options.axonometric.value_or(state.getAxonometric())) + .withXSkew(options.xSkew.value_or(state.getXSkew())) + .withYSkew(options.ySkew.value_or(state.getYSkew()))); } ProjectionMode Transform::getProjectionMode() const { return ProjectionMode() - .withAxonometric(state.axonometric) - .withXSkew(state.xSkew) - .withYSkew(state.ySkew); + .withAxonometric(state.getAxonometric()) + .withXSkew(state.getXSkew()) + .withYSkew(state.getYSkew()); } #pragma mark - Transition @@ -457,7 +478,8 @@ void Transform::startTransition(const CameraOptions& camera, } bool isAnimated = duration != Duration::zero(); - observer.onCameraWillChange(isAnimated ? MapObserver::CameraChangeMode::Animated : MapObserver::CameraChangeMode::Immediate); + observer.onCameraWillChange(isAnimated ? MapObserver::CameraChangeMode::Animated + : MapObserver::CameraChangeMode::Immediate); // Associate the anchor, if given, with a coordinate. // Anchor and center points are mutually exclusive, with preference for the @@ -465,7 +487,7 @@ void Transform::startTransition(const CameraOptions& camera, optional<ScreenCoordinate> anchor = camera.center ? nullopt : camera.anchor; LatLng anchorLatLng; if (anchor) { - anchor->y = state.size.height - anchor->y; + anchor->y = state.getSize().height - anchor->y; anchorLatLng = state.screenCoordinateToLatLng(*anchor); } @@ -497,13 +519,14 @@ void Transform::startTransition(const CameraOptions& camera, }; transitionFinishFn = [isAnimated, animation, this] { - state.panning = false; - state.scaling = false; - state.rotating = false; + state.setProperties( + TransformStateProperties().withPanningInProgress(false).withScalingInProgress(false).withRotatingInProgress( + false)); if (animation.transitionFinishFn) { animation.transitionFinishFn(); } - observer.onCameraDidChange(isAnimated ? MapObserver::CameraChangeMode::Animated : MapObserver::CameraChangeMode::Immediate); + observer.onCameraDidChange(isAnimated ? MapObserver::CameraChangeMode::Animated + : MapObserver::CameraChangeMode::Immediate); }; if (!isAnimated) { @@ -573,28 +596,27 @@ void Transform::cancelTransitions() { } void Transform::setGestureInProgress(bool inProgress) { - state.gestureInProgress = inProgress; + state.setGestureInProgress(inProgress); } #pragma mark Conversion and projection ScreenCoordinate Transform::latLngToScreenCoordinate(const LatLng& latLng) const { ScreenCoordinate point = state.latLngToScreenCoordinate(latLng); - point.y = state.size.height - point.y; + point.y = state.getSize().height - point.y; return point; } LatLng Transform::screenCoordinateToLatLng(const ScreenCoordinate& point, LatLng::WrapMode wrapMode) const { ScreenCoordinate flippedPoint = point; - flippedPoint.y = state.size.height - flippedPoint.y; + flippedPoint.y = state.getSize().height - flippedPoint.y; return state.screenCoordinateToLatLng(flippedPoint, wrapMode); } -double Transform::getMaxPitchForEdgeInsets(const EdgeInsets &insets) const -{ +double Transform::getMaxPitchForEdgeInsets(const EdgeInsets& insets) const { double centerOffsetY = 0.5 * (insets.top() - insets.bottom()); // See TransformState::getCenterOffset. - const auto height = state.size.height; + const auto height = state.getSize().height; assert(height); // For details, see description at https://github.com/mapbox/mapbox-gl-native/pull/15195 // The definition of half of TransformState::fov with no inset, is: fov = arctan((height / 2) / (height * 1.5)). |