Skip to content

Commit

Permalink
Base animation duration on actual distance estimation
Browse files Browse the repository at this point in the history
  • Loading branch information
Rawa committed Mar 22, 2024
1 parent 585c653 commit 9d35783
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 12 deletions.
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 @@ -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 }
}
}

0 comments on commit 9d35783

Please sign in to comment.