diff options
author | Łukasz Paczos <lukas.paczos@gmail.com> | 2019-08-15 17:34:04 +0200 |
---|---|---|
committer | Łukasz Paczos <lukasz.paczos@mapbox.com> | 2019-08-15 20:34:29 +0300 |
commit | 8e1bd18eae73ffbe14aa9297a039e2431d40bd36 (patch) | |
tree | 50749fe9e5efddfd0852d6dd6ac828f62d00a368 | |
parent | 074dba57a2405653191869fccddf19f304b03cf8 (diff) | |
download | qtlocation-mapboxgl-8e1bd18eae73ffbe14aa9297a039e2431d40bd36.tar.gz |
[android] execute quickzoom scale change based on the Y axis delta change
Using finger's Y axis position allows for a linear zoom additions/deductions during the quick-zoom changes. This is in contrast to the previously used scale factor, which is based on the current span (distance from the origin) to previous span ratio and increases the closer the finger is to the origin of a gesture because the values are smaller.
The scale ratio based changes are also reliant on series of motion events' values and because the Android framework can skip some events, going back and forth during a quick-zoom might not have resulted in the same transformation. This was reproduced by the round-tripping test introduced in this commit.
4 files changed, 68 insertions, 18 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index d2694b5b8a..6156b3af73 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -87,6 +87,11 @@ public class MapboxConstants { public static final double MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE = 2.5; /** + * Maximum possible zoom change during the quick zoom gesture executed across the whole screen + */ + public static final double QUICK_ZOOM_MAX_ZOOM_CHANGE = 4.0; + + /** * Scale velocity animation duration multiplier. */ public static final double SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER = 150; @@ -156,12 +161,18 @@ public class MapboxConstants { /** * The currently used minimum scale factor to clamp to when a quick zoom gesture occurs + * + * @deprecated unused */ + @Deprecated public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f; /** * The currently used maximum scale factor to clamp to when a quick zoom gesture occurs + * + * @deprecated unused */ + @Deprecated public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f; /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 0b28c669af..90e3934f7c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -4,6 +4,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; +import android.content.res.Resources; import android.graphics.PointF; import android.os.Handler; import android.support.annotation.NonNull; @@ -31,11 +32,13 @@ import java.util.concurrent.CopyOnWriteArrayList; import static com.mapbox.mapboxsdk.constants.MapboxConstants.MAXIMUM_ANGULAR_VELOCITY; import static com.mapbox.mapboxsdk.constants.MapboxConstants.MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE; +import static com.mapbox.mapboxsdk.constants.MapboxConstants.QUICK_ZOOM_MAX_ZOOM_CHANGE; import static com.mapbox.mapboxsdk.constants.MapboxConstants.ROTATE_VELOCITY_RATIO_THRESHOLD; import static com.mapbox.mapboxsdk.constants.MapboxConstants.SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER; import static com.mapbox.mapboxsdk.constants.MapboxConstants.SCALE_VELOCITY_RATIO_THRESHOLD; import static com.mapbox.mapboxsdk.constants.MapboxConstants.ZOOM_RATE; import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE; +import static com.mapbox.mapboxsdk.utils.MathUtils.normalize; /** * Manages gestures events on a MapView. @@ -76,6 +79,9 @@ final class MapGestureDetector { @Nullable private PointF constantFocalPoint; + @NonNull + private PointF doubleTapFocalPoint = new PointF(); + private AndroidGesturesManager gesturesManager; private Animator scaleAnimator; @@ -321,8 +327,6 @@ final class MapGestureDetector { } private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener { - - private PointF doubleTapFocalPoint; private final float doubleTapMovementThreshold; StandardGestureListener(float doubleTapMovementThreshold) { @@ -495,6 +499,8 @@ final class MapGestureDetector { private final double scaleVelocityRatioThreshold; private boolean quickZoom; private float spanSinceLast; + private double screenHeight; + private double startZoom; ScaleGestureListener(double densityMultiplier, float minimumGestureSpeed, float minimumAngledGestureSpeed, float minimumVelocity) { @@ -548,6 +554,9 @@ final class MapGestureDetector { } } + screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels; + startZoom = transform.getRawZoom(); + cancelTransitionsIfRequired(); notifyOnScaleBeginListeners(detector); @@ -562,10 +571,24 @@ final class MapGestureDetector { // dispatching camera start event only when the movement actually occurred cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); - float scaleFactor = detector.getScaleFactor(); - double zoomBy = getNewZoom(scaleFactor, quickZoom); PointF focalPoint = getScaleFocalPoint(detector); - transform.zoomBy(zoomBy, focalPoint); + if (quickZoom) { + double pixelDeltaChange = Math.abs(detector.getCurrentEvent().getY() - doubleTapFocalPoint.y); + boolean zoomedOut = detector.getCurrentEvent().getY() < doubleTapFocalPoint.y; + + // normalize the pixel delta change, ranging from 0 to screen height, to a constant zoom change range + double normalizedDeltaChange = normalize(pixelDeltaChange, 0, screenHeight, 0, QUICK_ZOOM_MAX_ZOOM_CHANGE); + + // calculate target zoom and adjust for a multiplier + double targetZoom = (zoomedOut ? startZoom - normalizedDeltaChange : startZoom + normalizedDeltaChange); + targetZoom *= uiSettings.getZoomRate(); + + transform.setZoom(targetZoom, focalPoint); + } else { + double zoomBy = + (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) * ZOOM_RATE * uiSettings.getZoomRate(); + transform.zoomBy(zoomBy, focalPoint); + } notifyOnScaleListeners(detector); @@ -628,19 +651,6 @@ final class MapGestureDetector { } return zoomAddition; } - - private double getNewZoom(float scaleFactor, boolean quickZoom) { - double zoomBy = (Math.log(scaleFactor) / Math.log(Math.PI / 2)) * ZOOM_RATE * uiSettings.getZoomRate(); - if (quickZoom) { - // clamp scale factors we feed to core #7514 - boolean negative = zoomBy < 0; - zoomBy = MathUtils.clamp(Math.abs(zoomBy), - MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP, - MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP); - return negative ? -zoomBy : zoomBy; - } - return zoomBy; - } } private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java index 0c90e4b244..7ec3262c57 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java @@ -46,4 +46,19 @@ public class MathUtils { return secondMod + min; } + + /** + * Scale a value from an arbitrary range to a normalized range. + * + * @param x The value to be normalized. + * @param dataLow lowest expected value from a data set + * @param dataHigh highest expected value from a data set + * @param normalizedLow normalized lowest value + * @param normalizedHigh normalized highest value + * @return The result of the normalization. + */ + public static double normalize(double x, double dataLow, double dataHigh, + double normalizedLow, double normalizedHigh) { + return ((x - dataLow) / (dataHigh - dataLow)) * (normalizedHigh - normalizedLow) + normalizedLow; + } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt index 3980199cad..525c576df4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt @@ -176,4 +176,18 @@ class MapGestureDetectorTest : BaseTest() { Assert.assertNotEquals(initialCameraPosition!!.target.longitude, mapboxMap.cameraPosition.target.longitude, 1.0) } } + + @Test + fun quickZoom_roundTripping() { + validateTestSetup() + rule.runOnUiThread { + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(51.0, 16.0), 3.0)) + } + onView(withId(R.id.mapView)).perform(quickScale(300f, withVelocity = false, duration = 750L)) + onView(withId(R.id.mapView)).perform(quickScale(-300f, withVelocity = false, duration = 750L)) + + rule.runOnUiThread { + Assert.assertEquals(3.0, mapboxMap.cameraPosition.zoom, 0.0001) + } + } }
\ No newline at end of file |