diff options
Diffstat (limited to 'android/lib/model/src')
6 files changed, 171 insertions, 0 deletions
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/LatLong.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/LatLong.kt index 19f757ffc3..a3cbf13761 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/LatLong.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/LatLong.kt @@ -46,3 +46,5 @@ data class LatLong(val latitude: Latitude, val longitude: Longitude) { const val COMPLETE_ANGLE = 360f fun Float.toRadians() = this * Math.PI.toFloat() / (COMPLETE_ANGLE / 2) + +fun Float.toDegrees() = this * ((COMPLETE_ANGLE / 2) / Math.PI.toFloat()) diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItem.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItem.kt index b1df67fea6..3ea0b48cbb 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItem.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItem.kt @@ -71,6 +71,7 @@ sealed interface RelayItem { override val id: GeoLocationId.City, override val name: String, val relays: List<Relay>, + val latLong: LatLong, ) : Location { override val active = relays.any { it.active } override val hasChildren: Boolean = relays.isNotEmpty() diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Ray.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Ray.kt new file mode 100644 index 0000000000..2e8eb236c8 --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Ray.kt @@ -0,0 +1,3 @@ +package net.mullvad.mullvadvpn.lib.model.map + +data class Ray(val origin: Vector3, val direction: Vector3) diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Sphere.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Sphere.kt new file mode 100644 index 0000000000..73a759e938 --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Sphere.kt @@ -0,0 +1,7 @@ +package net.mullvad.mullvadvpn.lib.model.map + +data class Sphere(val center: Vector3, val radius: Float) { + companion object { + const val RADIUS = 1f + } +} diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Vector3.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Vector3.kt new file mode 100644 index 0000000000..e1c3901a11 --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/map/Vector3.kt @@ -0,0 +1,89 @@ +package net.mullvad.mullvadvpn.lib.model.map + +import kotlin.math.acos +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.sin +import kotlin.math.sqrt +import net.mullvad.mullvadvpn.lib.model.LatLong +import net.mullvad.mullvadvpn.lib.model.Latitude +import net.mullvad.mullvadvpn.lib.model.Longitude +import net.mullvad.mullvadvpn.lib.model.toDegrees +import net.mullvad.mullvadvpn.lib.model.toRadians + +data class Vector3(val x: Float, val y: Float, val z: Float) { + fun dot(other: Vector3): Float { + return x * other.x + y * other.y + z * other.z + } + + operator fun minus(other: Vector3): Vector3 { + return Vector3(x - other.x, y - other.y, z - other.z) + } + + operator fun times(scalar: Float): Vector3 { + return Vector3(x * scalar, y * scalar, z * scalar) + } + + operator fun plus(other: Vector3): Vector3 { + return Vector3(x + other.x, y + other.y, z + other.z) + } + + fun normalize(): Vector3 { + val length = sqrt(x * x + y * y + z * z) + return Vector3(x / length, y / length, z / length) + } + + fun distanceTo(other: Vector3): Float { + val dx = x - other.x + val dy = y - other.y + val dz = z - other.z + return sqrt(dx * dx + dy * dy + dz * dz) + } +} + +fun Vector3.rotateAroundX(degrees: Float): Vector3 { + val radians = degrees.toRadians() + val cosTheta = cos(radians) + val sinTheta = sin(radians) + + val newY = cosTheta * y - sinTheta * z + val newZ = sinTheta * y + cosTheta * z + + return Vector3(x, -newY, newZ) +} + +fun Vector3.rotateAroundY(degrees: Float): Vector3 { + val radians = degrees.toRadians() + val cosTheta = cos(radians) + val sinTheta = sin(radians) + + val newX = cosTheta * x + sinTheta * z + val newZ = -sinTheta * x + cosTheta * z + + return Vector3(newX, y, -newZ) +} + +fun Vector3.toLatLng(): LatLong { + // phi + val lat = acos(y / Sphere.RADIUS) + + // theta + val lon = atan2(x, z) + + return LatLong( + // This worked for some reason (camera starts at lat 90!) + Latitude.fromFloat(90f - lat.toDegrees()), + Longitude.fromFloat(-lon.toDegrees()), + ) +} + +fun LatLong.toVector3(): Vector3 { + val phi = this.latitude.value.toRadians() + val theta = this.longitude.value.toRadians() + + val x = -Sphere.RADIUS * cos(phi) * sin(theta) + val y = Sphere.RADIUS * sin(phi) + val z = Sphere.RADIUS * cos(phi) * cos(theta) + + return Vector3(x, y, z) +} diff --git a/android/lib/model/src/test/kotlin/net/mullvad/mullvadvpn/lib/model/map/Vector3Test.kt b/android/lib/model/src/test/kotlin/net/mullvad/mullvadvpn/lib/model/map/Vector3Test.kt new file mode 100644 index 0000000000..c9f35692bf --- /dev/null +++ b/android/lib/model/src/test/kotlin/net/mullvad/mullvadvpn/lib/model/map/Vector3Test.kt @@ -0,0 +1,69 @@ +package net.mullvad.mullvadvpn.lib.model.map + +import kotlin.test.assertEquals +import net.mullvad.mullvadvpn.lib.model.LatLong +import net.mullvad.mullvadvpn.lib.model.Latitude +import net.mullvad.mullvadvpn.lib.model.Longitude +import org.junit.jupiter.api.Test + +class Vector3Test { + @Test + fun `Y-axis center test`() { + assertVector3Equals(Y_POSITIVE_CENTER.toVector3(), Vector3(0f, 1f, 0f)) + assertVector3Equals(Y_NEGATIVE_CENTER.toVector3(), Vector3(0f, -1f, 0f)) + } + + @Test + fun `Z-axis center test`() { + assertVector3Equals(Z_POSITIVE_CENTER.toVector3(), Vector3(0f, 0f, 1f)) + assertVector3Equals(Z_NEGATIVE_CENTER.toVector3(), Vector3(0f, 0f, -1f)) + } + + @Test + fun `X-axis center test`() { + assertVector3Equals(X_POSITIVE_CENTER.toVector3(), Vector3(-1f, 0f, 0f)) + assertVector3Equals(X_NEGATIVE_CENTER.toVector3(), Vector3(1f, 0f, 0f)) + } + + @Test + fun `Y-axis center to LatLng test`() { + assertLatLngEquals(Vector3(0f, 1f, 0f).toLatLng(), Y_POSITIVE_CENTER) + assertLatLngEquals(Vector3(0f, -1f, 0f).toLatLng(), Y_NEGATIVE_CENTER) + } + + @Test + fun `Z-axis center to LatLng test`() { + assertLatLngEquals(Vector3(0f, 0f, 1f).toLatLng(), Z_POSITIVE_CENTER) + assertLatLngEquals(Vector3(0f, 0f, -1f).toLatLng(), Z_NEGATIVE_CENTER) + } + + @Test + fun `X-axis center to LatLng test`() { + assertLatLngEquals(Vector3(-1f, 0f, 0f).toLatLng(), X_POSITIVE_CENTER) + assertLatLngEquals(Vector3(1f, 0f, 0f).toLatLng(), X_NEGATIVE_CENTER) + } + + companion object { + // NORTH POLE + val Y_POSITIVE_CENTER = LatLong(Latitude(90f), Longitude(0f)) + // SOUTH POLE + val Y_NEGATIVE_CENTER = LatLong(Latitude(-90f), Longitude(0f)) + + val Z_POSITIVE_CENTER = LatLong(Latitude(0f), Longitude(0f)) + val Z_NEGATIVE_CENTER = LatLong(Latitude(0f), Longitude.fromFloat(180f)) + + val X_NEGATIVE_CENTER = LatLong(Latitude(0f), Longitude.fromFloat(-90f)) + val X_POSITIVE_CENTER = LatLong(Latitude(0f), Longitude.fromFloat(90f)) + } + + fun assertVector3Equals(expected: Vector3, actual: Vector3) { + assertEquals(expected.x, actual.x, 0.0001f) + assertEquals(expected.y, actual.y, 0.0001f) + assertEquals(expected.z, actual.z, 0.0001f) + } + + fun assertLatLngEquals(expected: LatLong, actual: LatLong) { + assertEquals(expected.latitude.distanceTo(actual.latitude), 0f, 0.0001f) + assertEquals(expected.longitude.distanceTo(actual.longitude), 0f, 0.0001f) + } +} |
