Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve animation duration calculation #5991

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ fun animatedCameraPosition(
}

val distance =
remember(targetCameraLocation) { targetCameraLocation.distanceTo(previousLocation).toInt() }
val duration = distance.toAnimationDuration()
remember(targetCameraLocation) { targetCameraLocation.seppDistanceTo(previousLocation) }
val duration = distance.toAnimationDurationMillis()

val longitudeAnimation = remember { Animatable(targetCameraLocation.longitude.value) }

Expand Down Expand Up @@ -97,5 +97,7 @@ fun animatedCameraPosition(
)
}

private fun Int.toAnimationDuration() =
(this * DISTANCE_DURATION_SCALE_FACTOR).coerceIn(MIN_ANIMATION_MILLIS, MAX_ANIMATION_MILLIS)
private fun Float.toAnimationDurationMillis(): Int =
(this * DISTANCE_DURATION_SCALE_FACTOR)
.toInt()
.coerceIn(MIN_ANIMATION_MILLIS, MAX_ANIMATION_MILLIS)
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ internal const val VERTEX_COMPONENT_SIZE = 3
internal const val COLOR_COMPONENT_SIZE = 4
internal const val MATRIX_SIZE = 16

// Constant what will talk the distance in LatLng multiply it to determine the animation duration,
// Constant what will take the distance in km between two LatLong, multiply it to determine the
// animation duration,
// the result is then confined to the MIN_ANIMATION_MILLIS and MAX_ANIMATION_MILLIS
internal const val DISTANCE_DURATION_SCALE_FACTOR = 20
internal const val DISTANCE_DURATION_SCALE_FACTOR = 0.4f
internal const val MIN_ANIMATION_MILLIS = 1300
internal const val MAX_ANIMATION_MILLIS = 2500
// The cut off where we go from a short animation (camera pans) to a far animation (camera pans +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import net.mullvad.mullvadvpn.lib.map.data.LocationMarkerColors
import net.mullvad.mullvadvpn.lib.map.data.MapViewState
import net.mullvad.mullvadvpn.lib.map.internal.shapes.Globe
import net.mullvad.mullvadvpn.lib.map.internal.shapes.LocationMarker
import net.mullvad.mullvadvpn.model.COMPLETE_ANGLE
import net.mullvad.mullvadvpn.model.toRadians

internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.Renderer {

Expand Down Expand Up @@ -79,8 +79,6 @@ internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.R
}
}

private fun Float.toRadians() = this * Math.PI.toFloat() / (COMPLETE_ANGLE / 2)

private fun toOffsetY(cameraPosition: CameraPosition): Float {
val percent = cameraPosition.verticalBias
val z = cameraPosition.zoom - 1f
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package net.mullvad.mullvadvpn.model

import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sqrt
import net.mullvad.mullvadvpn.model.Latitude.Companion.mean

data class LatLong(val latitude: Latitude, val longitude: Longitude) {

fun distanceTo(other: LatLong): Float =
fun degreeDistanceTo(other: LatLong): Float =
sqrt(
latitude.distanceTo(other.latitude).pow(2f) +
(longitude.distanceTo(other.longitude).pow(2f))
Expand All @@ -16,6 +18,31 @@ data class LatLong(val latitude: Latitude, val longitude: Longitude) {

operator fun minus(other: LatLong) =
LatLong(latitude - other.latitude, longitude - other.longitude)

/**
* Calculate the distance between two points on the earth's surface using the spherical earth
* projected to a plane. ( This method has some drawbacks and shortcomings for extreme values
* closer to the Poles but should be good enough for our use case. ) Reference:
* https://en.wikipedia.org/wiki/Geographical_distance#Spherical_Earth_projected_to_a_plane
*
* @param other the other point to calculate the distance to.
* @return the estimated distance in kilometers.
*/
fun seppDistanceTo(other: LatLong): Float =
EARTH_RADIUS *
sqrt(
latitude.distanceTo(other.latitude).toRadians().pow(2) +
(cos(mean(latitude, other.latitude).value.toRadians()) *
longitude.distanceTo(other.longitude).toRadians())
.pow(2)
)

companion object {
// Average radius of the earth in kilometers
const val EARTH_RADIUS = 6371.009f
}
}

const val COMPLETE_ANGLE = 360f

fun Float.toRadians() = this * Math.PI.toFloat() / (COMPLETE_ANGLE / 2)
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ value class Latitude(val value: Float) {
private const val MAX_LATITUDE_VALUE: Float = 90f
private val LATITUDE_RANGE = MIN_LATITUDE_VALUE..MAX_LATITUDE_VALUE

fun mean(latitude1: Latitude, latitude2: Latitude): Latitude =
fromFloat((latitude1.value + latitude2.value) / 2)

/**
* Create a [Latitude] from a float value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,40 @@ package net.mullvad.mullvadvpn.model

import kotlin.math.sqrt
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class LatLongTest {

@Test
fun `distance between two LatLong should be same as hypotenuse`() {
fun `degree distance between two LatLong should be same as hypotenuse`() {
val latLong1 = LatLong(Latitude(30f), Longitude(40f))
val latLong2 = LatLong(Latitude(-40f), Longitude(170f))

val latDiff = latLong1.latitude.distanceTo(latLong2.latitude)
val longDiff = latLong1.longitude.distanceTo(latLong2.longitude)
val hypotenuse = sqrt(latDiff * latDiff + longDiff * longDiff)

assertEquals(hypotenuse, latLong1.distanceTo(latLong2))
assertEquals(hypotenuse, latLong1.degreeDistanceTo(latLong2))
}

@Test
fun `ensure seppDistance respects lateral value`() {
// Malmö & New York is a shorter distance than Malmö & Johannesburg, but the degree
// difference is larger since they are at a higher latitude.

// Malmo 55.6050° N, 13.0038° E
val malmo = LatLong(Latitude(55.6050f), Longitude(13.0038f))

// New York 40.7128° N, 74.0060° W
val newYork = LatLong(Latitude(40.7128f), Longitude(-74.0060f))

// Johannesburg 26.2041° S, 28.0473° E
val johannesburg = LatLong(Latitude(-26.2041f), Longitude(28.0473f))

val malmoToNewYork = malmo.seppDistanceTo(newYork)
val malmoToJohannesburg = malmo.seppDistanceTo(johannesburg)

assertTrue { malmoToNewYork < malmoToJohannesburg }
}
}
Loading