From 9f3a384296f019b1b3719b39389ae5be995a4d37 Mon Sep 17 00:00:00 2001 From: zmiao Date: Thu, 5 Dec 2019 18:58:48 +0200 Subject: [core] Refactoring transform state (#15956) * [core] Refactoring transform state class * [core] update matrix in transform, Fix precision * [core] Make matrix mutable so that we only update them when needed * [core] Add getters for matrices --- .../memory/pass-memory-size-is-same/metrics.json | 4 +- .../memory/pass-memory-size-is-same/metrics.json | 4 +- src/mbgl/map/transform.cpp | 274 ++++++++++---------- src/mbgl/map/transform.hpp | 5 +- src/mbgl/map/transform_state.cpp | 276 +++++++++++++++++---- src/mbgl/map/transform_state.hpp | 152 ++++++++++-- src/mbgl/renderer/renderer_state.cpp | 4 +- src/mbgl/util/mat4.cpp | 2 +- src/mbgl/util/mat4.hpp | 2 +- 9 files changed, 526 insertions(+), 197 deletions(-) diff --git a/render-test/metrics/mac/tests/memory/pass-memory-size-is-same/metrics.json b/render-test/metrics/mac/tests/memory/pass-memory-size-is-same/metrics.json index cc7d1c9fab..3ec709ae28 100644 --- a/render-test/metrics/mac/tests/memory/pass-memory-size-is-same/metrics.json +++ b/render-test/metrics/mac/tests/memory/pass-memory-size-is-same/metrics.json @@ -2,12 +2,12 @@ "memory": [ [ "after setZoom 0.9", - 33271, + 34295, 51 ], [ "end", - 47552, + 49120, 101 ], [ diff --git a/render-test/tests/memory/pass-memory-size-is-same/metrics.json b/render-test/tests/memory/pass-memory-size-is-same/metrics.json index 4baa38c7f7..fdd2deb3ff 100644 --- a/render-test/tests/memory/pass-memory-size-is-same/metrics.json +++ b/render-test/tests/memory/pass-memory-size-is-same/metrics.json @@ -2,12 +2,12 @@ "memory": [ [ "after setZoom 0.9", - 33271, + 34295, 62 ], [ "end", - 47833, + 49401, 118 ], [ 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 startPoint = Projection::project(startLatLng, state.scale); - const Point endPoint = Projection::project(latLng, state.scale); + const Point startPoint = Projection::project(startLatLng, state.getScale()); + const Point 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 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 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 startPoint = Projection::project(startLatLng, state.scale); - const Point endPoint = Projection::project(latLng, state.scale); + const Point startPoint = Projection::project(startLatLng, state.getScale()); + const Point 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 screenful. - 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 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 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 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)). diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 30ce8a37a4..3dd1a3ffc5 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -114,10 +114,7 @@ private: MapObserver& observer; TransformState state; - void startTransition(const CameraOptions&, - const AnimationOptions&, - std::function, - const Duration&); + void startTransition(const CameraOptions&, const AnimationOptions&, std::function, const Duration&); // We don't want to show horizon: limit max pitch based on edge insets. double getMaxPitchForEdgeInsets(const EdgeInsets &insets) const; diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index f3cd8c3886..c8f1c7f594 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -8,10 +8,60 @@ #include namespace mbgl { - TransformState::TransformState(ConstrainMode constrainMode_, ViewportMode viewportMode_) : bounds(LatLngBounds()), constrainMode(constrainMode_), viewportMode(viewportMode_) {} +void TransformState::setProperties(const TransformStateProperties& properties) { + if (properties.x) { + setX(*properties.x); + } + if (properties.y) { + setY(*properties.y); + } + if (properties.scale) { + setScale(*properties.scale); + } + if (properties.bearing) { + setBearing(*properties.bearing); + } + if (properties.pitch) { + setPitch(*properties.pitch); + } + if (properties.xSkew) { + setXSkew(*properties.xSkew); + } + if (properties.ySkew) { + setYSkew(*properties.ySkew); + } + if (properties.axonometric) { + setAxonometric(*properties.axonometric); + } + if (properties.panning) { + setPanningInProgress(*properties.panning); + } + if (properties.scaling) { + setScalingInProgress(*properties.scaling); + } + if (properties.rotating) { + setRotatingInProgress(*properties.rotating); + } + if (properties.edgeInsets) { + setEdgeInsets(*properties.edgeInsets); + } + if (properties.size) { + setSize(*properties.size); + } + if (properties.constrain) { + setConstrainMode(*properties.constrain); + } + if (properties.northOrientation) { + setNorthOrientation(*properties.northOrientation); + } + if (properties.viewPortMode) { + setViewportMode(*properties.viewPortMode); + } +} + #pragma mark - Matrix void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) const { @@ -19,9 +69,8 @@ void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) cons const double s = Projection::worldSize(scale) / tileScale; matrix::identity(matrix); - matrix::translate(matrix, matrix, - int64_t(tileID.canonical.x + tileID.wrap * tileScale) * s, - int64_t(tileID.canonical.y) * s, 0); + matrix::translate( + matrix, matrix, int64_t(tileID.canonical.x + tileID.wrap * tileScale) * s, int64_t(tileID.canonical.y) * s, 0); matrix::scale(matrix, matrix, s / util::EXTENT, s / util::EXTENT, 1); } @@ -65,10 +114,18 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ, bool aligne using NO = NorthOrientation; switch (getNorthOrientation()) { - case NO::Rightwards: matrix::rotate_y(projMatrix, projMatrix, getPitch()); break; - case NO::Downwards: matrix::rotate_x(projMatrix, projMatrix, -getPitch()); break; - case NO::Leftwards: matrix::rotate_y(projMatrix, projMatrix, -getPitch()); break; - default: matrix::rotate_x(projMatrix, projMatrix, getPitch()); break; + case NO::Rightwards: + matrix::rotate_y(projMatrix, projMatrix, getPitch()); + break; + case NO::Downwards: + matrix::rotate_x(projMatrix, projMatrix, -getPitch()); + break; + case NO::Leftwards: + matrix::rotate_y(projMatrix, projMatrix, -getPitch()); + break; + default: + matrix::rotate_x(projMatrix, projMatrix, getPitch()); + break; } matrix::rotate_z(projMatrix, projMatrix, getBearing() + getNorthOrientationAngle()); @@ -104,18 +161,59 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ, bool aligne } } +void TransformState::updateMatricesIfNeeded() const { + if (!needsMatricesUpdate() || size.isEmpty()) return; + + getProjMatrix(projectionMatrix); + coordMatrix = coordinatePointMatrix(projectionMatrix); + + bool err = matrix::invert(invertedMatrix, coordMatrix); + + if (err) throw std::runtime_error("failed to invert coordinatePointMatrix"); + requestMatricesUpdate = false; +} + +const mat4& TransformState::getProjectionMatrix() const { + updateMatricesIfNeeded(); + return projectionMatrix; +} + +const mat4& TransformState::getCoordMatrix() const { + updateMatricesIfNeeded(); + return coordMatrix; +} + +const mat4& TransformState::getInvertedMatrix() const { + updateMatricesIfNeeded(); + return invertedMatrix; +} + #pragma mark - Dimensions Size TransformState::getSize() const { return size; } +void TransformState::setSize(const Size& size_) { + if (size != size_) { + size = size_; + requestMatricesUpdate = true; + } +} + #pragma mark - North Orientation NorthOrientation TransformState::getNorthOrientation() const { return orientation; } +void TransformState::setNorthOrientation(const NorthOrientation val) { + if (orientation != val) { + orientation = val; + requestMatricesUpdate = true; + } +} + double TransformState::getNorthOrientationAngle() const { double angleOrientation = 0; if (orientation == NorthOrientation::Rightwards) { @@ -134,12 +232,26 @@ ConstrainMode TransformState::getConstrainMode() const { return constrainMode; } +void TransformState::setConstrainMode(const ConstrainMode val) { + if (constrainMode != val) { + constrainMode = val; + requestMatricesUpdate = true; + } +} + #pragma mark - ViewportMode ViewportMode TransformState::getViewportMode() const { return viewportMode; } +void TransformState::setViewportMode(ViewportMode val) { + if (viewportMode != val) { + viewportMode = val; + requestMatricesUpdate = true; + } +} + #pragma mark - Camera options CameraOptions TransformState::getCameraOptions(optional padding) const { @@ -151,14 +263,19 @@ CameraOptions TransformState::getCameraOptions(optional padding) con .withPitch(pitch * util::RAD2DEG); } +#pragma mark - EdgeInsets + +void TransformState::setEdgeInsets(const EdgeInsets& val) { + if (edgeInsets != val) { + edgeInsets = val; + requestMatricesUpdate = true; + } +} + #pragma mark - Position LatLng TransformState::getLatLng(LatLng::WrapMode wrapMode) const { - return { - util::RAD2DEG * (2 * std::atan(std::exp(y / Cc)) - 0.5 * M_PI), - -x / Bc, - wrapMode - }; + return {util::RAD2DEG * (2 * std::atan(std::exp(y / Cc)) - 0.5 * M_PI), -x / Bc, wrapMode}; } double TransformState::pixel_x() const { @@ -223,12 +340,55 @@ double TransformState::getMaxZoom() const { return scaleZoom(max_scale); } +#pragma mark - Scale +double TransformState::getScale() const { + return scale; +} + +void TransformState::setScale(double val) { + if (scale != val) { + scale = val; + requestMatricesUpdate = true; + } +} + +#pragma mark - Positions + +double TransformState::getX() const { + return x; +} + +void TransformState::setX(double val) { + if (x != val) { + x = val; + requestMatricesUpdate = true; + } +} + +double TransformState::getY() const { + return y; +} + +void TransformState::setY(double val) { + if (y != val) { + y = val; + requestMatricesUpdate = true; + } +} + #pragma mark - Rotation -float TransformState::getBearing() const { +double TransformState::getBearing() const { return bearing; } +void TransformState::setBearing(double val) { + if (bearing != val) { + bearing = val; + requestMatricesUpdate = true; + } +} + float TransformState::getFieldOfView() const { return fov; } @@ -237,10 +397,49 @@ float TransformState::getCameraToCenterDistance() const { return 0.5 * size.height / std::tan(fov / 2.0); } -float TransformState::getPitch() const { +double TransformState::getPitch() const { return pitch; } +void TransformState::setPitch(double val) { + if (pitch != val) { + pitch = val; + requestMatricesUpdate = true; + } +} + +double TransformState::getXSkew() const { + return xSkew; +} + +void TransformState::setXSkew(double val) { + if (xSkew != val) { + xSkew = val; + requestMatricesUpdate = true; + } +} +double TransformState::getYSkew() const { + return ySkew; +} + +void TransformState::setYSkew(double val) { + if (ySkew != val) { + ySkew = val; + requestMatricesUpdate = true; + } +} + +bool TransformState::getAxonometric() const { + return axonometric; +} + +void TransformState::setAxonometric(bool val) { + if (axonometric != val) { + axonometric = val; + requestMatricesUpdate = true; + } +} + #pragma mark - State bool TransformState::isChanging() const { @@ -278,26 +477,19 @@ ScreenCoordinate TransformState::latLngToScreenCoordinate(const LatLng& latLng) return {}; } - mat4 mat = coordinatePointMatrix(); vec4 p; Point pt = Projection::project(latLng, scale) / util::tileSize; - vec4 c = {{ pt.x, pt.y, 0, 1 }}; - matrix::transformMat4(p, c, mat); - return { p[0] / p[3], size.height - p[1] / p[3] }; + vec4 c = {{pt.x, pt.y, 0, 1}}; + matrix::transformMat4(p, c, getCoordMatrix()); + return {p[0] / p[3], size.height - p[1] / p[3]}; } TileCoordinate TransformState::screenCoordinateToTileCoordinate(const ScreenCoordinate& point, uint8_t atZoom) const { if (size.isEmpty()) { - return { {}, 0 }; + return {{}, 0}; } float targetZ = 0; - mat4 mat = coordinatePointMatrix(); - - mat4 inverted; - bool err = matrix::invert(inverted, mat); - - if (err) throw std::runtime_error("failed to invert coordinatePointMatrix"); double flippedY = size.height - point.y; @@ -307,10 +499,10 @@ TileCoordinate TransformState::screenCoordinateToTileCoordinate(const ScreenCoor vec4 coord0; vec4 coord1; - vec4 point0 = {{ point.x, flippedY, 0, 1 }}; - vec4 point1 = {{ point.x, flippedY, 1, 1 }}; - matrix::transformMat4(coord0, point0, inverted); - matrix::transformMat4(coord1, point1, inverted); + vec4 point0 = {{point.x, flippedY, 0, 1}}; + vec4 point1 = {{point.x, flippedY, 1, 1}}; + matrix::transformMat4(coord0, point0, getInvertedMatrix()); + matrix::transformMat4(coord1, point1, getInvertedMatrix()); double w0 = coord0[3]; double w1 = coord1[3]; @@ -323,7 +515,7 @@ TileCoordinate TransformState::screenCoordinateToTileCoordinate(const ScreenCoor double t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0); Point p = util::interpolate(p0, p1, t) / scale * static_cast(1 << atZoom); - return { { p.x, p.y }, static_cast(atZoom) }; + return {{p.x, p.y}, static_cast(atZoom)}; } LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, LatLng::WrapMode wrapMode) const { @@ -331,9 +523,8 @@ LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, L return Projection::unproject(coord.p, 1 / util::tileSize, wrapMode); } -mat4 TransformState::coordinatePointMatrix() const { - mat4 proj; - getProjMatrix(proj); +mat4 TransformState::coordinatePointMatrix(const mat4& projMatrix) const { + mat4 proj = projMatrix; matrix::scale(proj, proj, util::tileSize, util::tileSize, 1); matrix::multiply(proj, getPixelMatrix(), proj); return proj; @@ -342,13 +533,11 @@ mat4 TransformState::coordinatePointMatrix() const { mat4 TransformState::getPixelMatrix() const { mat4 m; matrix::identity(m); - matrix::scale(m, m, - static_cast(size.width) / 2, -static_cast(size.height) / 2, 1); + matrix::scale(m, m, static_cast(size.width) / 2, -static_cast(size.height) / 2, 1); matrix::translate(m, m, 1, -1, 0); return m; } - #pragma mark - (private helper functions) bool TransformState::rotatedNorth() const { @@ -377,7 +566,7 @@ void TransformState::constrain(double& scale_, double& x_, double& y_) const { } ScreenCoordinate TransformState::getCenterOffset() const { - return { 0.5 * (edgeInsets.left() - edgeInsets.right()), 0.5 * (edgeInsets.top() - edgeInsets.bottom()) }; + return {0.5 * (edgeInsets.left() - edgeInsets.right()), 0.5 * (edgeInsets.top() - edgeInsets.bottom())}; } void TransformState::moveLatLng(const LatLng& latLng, const ScreenCoordinate& anchor) { @@ -406,7 +595,7 @@ void TransformState::setLatLngZoom(const LatLng& latLng, double zoom) { setScalePoint(newScale, point); } -void TransformState::setScalePoint(const double newScale, const ScreenCoordinate &point) { +void TransformState::setScalePoint(const double newScale, const ScreenCoordinate& point) { double constrainedScale = newScale; ScreenCoordinate constrainedPoint = point; constrain(constrainedScale, constrainedPoint.x, constrainedPoint.y); @@ -416,14 +605,13 @@ void TransformState::setScalePoint(const double newScale, const ScreenCoordinate y = constrainedPoint.y; Bc = Projection::worldSize(scale) / util::DEGREES_MAX; Cc = Projection::worldSize(scale) / util::M2PI; + requestMatricesUpdate = true; } float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) const { - mat4 projectionMatrix; - getProjMatrix(projectionMatrix); mat4 tileProjectionMatrix; matrixFor(tileProjectionMatrix, tileID); - matrix::multiply(tileProjectionMatrix, projectionMatrix, tileProjectionMatrix); + matrix::multiply(tileProjectionMatrix, getProjectionMatrix(), tileProjectionMatrix); vec4 tileCenter = {{util::tileSize / 2, util::tileSize / 2, 0, 1}}; vec4 projectedCenter; matrix::transformMat4(projectedCenter, tileCenter, tileProjectionMatrix); @@ -435,11 +623,11 @@ float TransformState::maxPitchScaleFactor() const { return {}; } auto latLng = screenCoordinateToLatLng({ 0, static_cast(getSize().height) }); - mat4 mat = coordinatePointMatrix(); + Point pt = Projection::project(latLng, scale) / util::tileSize; vec4 p = {{ pt.x, pt.y, 0, 1 }}; vec4 topPoint; - matrix::transformMat4(topPoint, p, mat); + matrix::transformMat4(topPoint, p, getCoordMatrix()); return topPoint[3] / getCameraToCenterDistance(); } diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 10a92187d5..edc7f3b9cf 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -19,32 +19,123 @@ namespace mbgl { class UnwrappedTileID; class TileCoordinate; -class TransformState { - friend class Transform; - friend class RendererState; +struct TransformStateProperties { + TransformStateProperties& withX(const optional& val) { + x = val; + return *this; + } + TransformStateProperties& withY(const optional& val) { + y = val; + return *this; + } + TransformStateProperties& withScale(const optional& val) { + scale = val; + return *this; + } + TransformStateProperties& withBearing(const optional& val) { + bearing = val; + return *this; + } + TransformStateProperties& withPitch(const optional& val) { + pitch = val; + return *this; + } + TransformStateProperties& withXSkew(const optional& val) { + xSkew = val; + return *this; + } + TransformStateProperties& withYSkew(const optional& val) { + ySkew = val; + return *this; + } + TransformStateProperties& withAxonometric(const optional& val) { + axonometric = val; + return *this; + } + TransformStateProperties& withPanningInProgress(const optional& val) { + panning = val; + return *this; + } + TransformStateProperties& withScalingInProgress(const optional& val) { + scaling = val; + return *this; + } + TransformStateProperties& withRotatingInProgress(const optional& val) { + rotating = val; + return *this; + } + TransformStateProperties& withEdgeInsets(const optional& val) { + edgeInsets = val; + return *this; + } + TransformStateProperties& withSize(const optional& val) { + size = val; + return *this; + } + TransformStateProperties& withConstrainMode(const optional& val) { + constrain = val; + return *this; + } + TransformStateProperties& withNorthOrientation(const optional& val) { + northOrientation = val; + return *this; + } + TransformStateProperties& withViewportMode(const optional& val) { + viewPortMode = val; + return *this; + } + + optional x; + optional y; + optional bearing; + optional scale; + optional pitch; + optional xSkew; + optional ySkew; + optional axonometric; + optional panning; + optional scaling; + optional rotating; + optional edgeInsets; + optional size; + optional constrain; + optional northOrientation; + optional viewPortMode; +}; +class TransformState { public: TransformState(ConstrainMode = ConstrainMode::HeightOnly, ViewportMode = ViewportMode::Default); + void setProperties(const TransformStateProperties& properties); + // Matrix void matrixFor(mat4&, const UnwrappedTileID&) const; void getProjMatrix(mat4& matrix, uint16_t nearZ = 1, bool aligned = false) const; // Dimensions Size getSize() const; + void setSize(const Size& size_); // North Orientation NorthOrientation getNorthOrientation() const; double getNorthOrientationAngle() const; + void setNorthOrientation(const NorthOrientation); // Constrain mode ConstrainMode getConstrainMode() const; + void setConstrainMode(const ConstrainMode); // Viewport mode ViewportMode getViewportMode() const; + void setViewportMode(ViewportMode val); CameraOptions getCameraOptions(optional) const; + // EdgeInsects + EdgeInsets getEdgeInsets() const { return edgeInsets; } + void setEdgeInsets(const EdgeInsets&); + // Position LatLng getLatLng(LatLng::WrapMode = LatLng::Unwrapped) const; double pixel_x() const; @@ -56,7 +147,14 @@ public: double getZoomFraction() const; // Scale - double getScale() const { return scale; } + double getScale() const; + void setScale(double); + + // Positions + double getX() const; + void setX(double); + double getY() const; + void setY(double); // Bounds void setLatLngBounds(LatLngBounds); @@ -67,17 +165,30 @@ public: double getMaxZoom() const; // Rotation - float getBearing() const; + double getBearing() const; + void setBearing(double); float getFieldOfView() const; float getCameraToCenterDistance() const; - float getPitch() const; + double getPitch() const; + void setPitch(double); + + double getXSkew() const; + void setXSkew(double); + double getYSkew() const; + void setYSkew(double); + bool getAxonometric() const; + void setAxonometric(bool); // State bool isChanging() const; bool isRotating() const; + void setRotatingInProgress(bool val) { rotating = val; } bool isScaling() const; + void setScalingInProgress(bool val) { scaling = val; } bool isPanning() const; + void setPanningInProgress(bool val) { panning = val; } bool isGestureInProgress() const; + void setGestureInProgress(bool val) { gestureInProgress = val; } // Conversion ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const; @@ -88,16 +199,20 @@ public: double zoomScale(double zoom) const; double scaleZoom(double scale) const; - bool valid() const { - return !size.isEmpty() && (scale >= min_scale && scale <= max_scale); - } + bool valid() const { return !size.isEmpty() && (scale >= min_scale && scale <= max_scale); } float getCameraToTileDistance(const UnwrappedTileID&) const; float maxPitchScaleFactor() const; + /** Recenter the map so that the given coordinate is located at the given + point on screen. */ + void moveLatLng(const LatLng&, const ScreenCoordinate&); + void setLatLngZoom(const LatLng& latLng, double zoom); + + void constrain(double& scale, double& x, double& y) const; + private: bool rotatedNorth() const; - void constrain(double& scale, double& x, double& y) const; // Viewport center offset, from [size.width / 2, size.height / 2], defined // by |edgeInsets| in screen coordinates, with top left origin. @@ -114,15 +229,17 @@ private: // logical dimensions Size size; - mat4 coordinatePointMatrix() const; + mat4 coordinatePointMatrix(const mat4& projMatrix) const; mat4 getPixelMatrix() const; - /** Recenter the map so that the given coordinate is located at the given - point on screen. */ - void moveLatLng(const LatLng&, const ScreenCoordinate&); - void setLatLngZoom(const LatLng &latLng, double zoom); void setScalePoint(const double scale, const ScreenCoordinate& point); + void updateMatricesIfNeeded() const; + bool needsMatricesUpdate() const { return requestMatricesUpdate; } + const mat4& getProjectionMatrix() const; + const mat4& getCoordMatrix() const; + const mat4& getInvertedMatrix() const; + private: ConstrainMode constrainMode; ViewportMode viewportMode; @@ -152,6 +269,11 @@ private: // cache values for spherical mercator math double Bc = Projection::worldSize(scale) / util::DEGREES_MAX; double Cc = Projection::worldSize(scale) / util::M2PI; + + mutable bool requestMatricesUpdate{true}; + mutable mat4 projectionMatrix; + mutable mat4 coordMatrix; + mutable mat4 invertedMatrix; }; } // namespace mbgl diff --git a/src/mbgl/renderer/renderer_state.cpp b/src/mbgl/renderer/renderer_state.cpp index 33f6eb27dd..4cbe9de9c5 100644 --- a/src/mbgl/renderer/renderer_state.cpp +++ b/src/mbgl/renderer/renderer_state.cpp @@ -45,12 +45,12 @@ ScreenCoordinate RendererState::pixelForLatLng(const UpdateParameters& updatePar LatLng unwrappedLatLng = latLng.wrapped(); unwrappedLatLng.unwrapForShortestPath(updateParameters.transformState.getLatLng()); const ScreenCoordinate point = updateParameters.transformState.latLngToScreenCoordinate(latLng); - return ScreenCoordinate { point.x, updateParameters.transformState.size.height - point.y }; + return ScreenCoordinate{point.x, updateParameters.transformState.getSize().height - point.y}; } LatLng RendererState::latLngForPixel(const UpdateParameters& updateParameters, const ScreenCoordinate& point) { ScreenCoordinate flippedPoint = point; - flippedPoint.y = updateParameters.transformState.size.height - flippedPoint.y; + flippedPoint.y = updateParameters.transformState.getSize().height - flippedPoint.y; return updateParameters.transformState.screenCoordinateToLatLng(flippedPoint); } diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp index 0ad0d371e5..2f7d35a5be 100644 --- a/src/mbgl/util/mat4.cpp +++ b/src/mbgl/util/mat4.cpp @@ -47,7 +47,7 @@ void identity(mat4& out) { out[15] = 1.0f; } -bool invert(mat4& out, mat4& a) { +bool invert(mat4& out, const mat4& a) { double a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], diff --git a/src/mbgl/util/mat4.hpp b/src/mbgl/util/mat4.hpp index 2adeba8821..438771949f 100644 --- a/src/mbgl/util/mat4.hpp +++ b/src/mbgl/util/mat4.hpp @@ -32,7 +32,7 @@ using mat4 = std::array; namespace matrix { void identity(mat4& out); -bool invert(mat4& out, mat4& a); +bool invert(mat4& out, const mat4& a); void ortho(mat4& out, double left, double right, double bottom, double top, double near, double far); void perspective(mat4& out, double fovy, double aspect, double near, double far); void copy(mat4& out, const mat4& a); -- cgit v1.2.1