summaryrefslogtreecommitdiffhomepage
path: root/android/lib/map/src/main
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2025-08-29 14:51:10 +0200
committerDavid Göransson <david.goransson@mullvad.net>2025-09-01 08:34:16 +0200
commite02d460164fe09d5cf085cdcb3f40a9251bae633 (patch)
treecbad368a9d30876f24631a6548e7d32e2e2c9524 /android/lib/map/src/main
parent4f03db1de3224d666cf6fc5ab255f3629435a59a (diff)
downloadmullvadvpn-interactive-maps.tar.xz
mullvadvpn-interactive-maps.zip
Add interactive mapsinteractive-maps
Diffstat (limited to 'android/lib/map/src/main')
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/CameraAnimation.kt5
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/Map.kt42
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/CameraPosition.kt11
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/LocationMarkerColors.kt2
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/Marker.kt3
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/Constants.kt6
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLRenderer.kt101
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLSurfaceView.kt10
-rw-r--r--android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/shapes/LocationMarker.kt47
9 files changed, 199 insertions, 28 deletions
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/CameraAnimation.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/CameraAnimation.kt
index 2e0b2fcf05..0963ec1aa1 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/CameraAnimation.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/CameraAnimation.kt
@@ -88,16 +88,17 @@ fun animatedCameraPosition(
return CameraPosition(
zoom = baseZoom * zoomOutMultiplier.value,
+ verticalBias = cameraVerticalBias,
latLong =
LatLong(
Latitude(latitudeAnimation.value),
Longitude.fromFloat(longitudeAnimation.value),
),
- verticalBias = cameraVerticalBias,
+ // verticalBias = cameraVerticalBias,
)
}
-private fun Float.toAnimationDurationMillis(): Int =
+fun Float.toAnimationDurationMillis(): Int =
(this * DISTANCE_DURATION_SCALE_FACTOR)
.toInt()
.coerceIn(MIN_ANIMATION_MILLIS, MAX_ANIMATION_MILLIS)
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/Map.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/Map.kt
index 05b8d59701..76e84dca98 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/Map.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/Map.kt
@@ -1,19 +1,25 @@
package net.mullvad.mullvadvpn.lib.map
+import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
+import co.touchlab.kermit.Logger
import net.mullvad.mullvadvpn.lib.map.data.CameraPosition
import net.mullvad.mullvadvpn.lib.map.data.GlobeColors
import net.mullvad.mullvadvpn.lib.map.data.MapViewState
import net.mullvad.mullvadvpn.lib.map.data.Marker
import net.mullvad.mullvadvpn.lib.map.internal.MapGLSurfaceView
+import net.mullvad.mullvadvpn.lib.model.GeoLocationId
import net.mullvad.mullvadvpn.lib.model.LatLong
+import net.mullvad.mullvadvpn.lib.model.RelayItemId
@Composable
fun Map(
@@ -21,9 +27,11 @@ fun Map(
cameraLocation: CameraPosition,
markers: List<Marker>,
globeColors: GlobeColors,
+ onClickRelayItemId: (GeoLocationId) -> Unit,
+ onLongClickRelayItemId: (Offset, GeoLocationId) -> Unit,
) {
val mapViewState = MapViewState(cameraLocation, markers, globeColors)
- Map(modifier = modifier, mapViewState = mapViewState)
+ Map(modifier = modifier, mapViewState = mapViewState, onClickRelayItemId, onLongClickRelayItemId)
}
@Composable
@@ -34,6 +42,8 @@ fun AnimatedMap(
cameraVerticalBias: Float,
markers: List<Marker>,
globeColors: GlobeColors,
+ onClickRelayItemId: (RelayItemId) -> Unit,
+ onLongClickRelayItemId: (Offset, RelayItemId) -> Unit
) {
Map(
modifier = modifier,
@@ -45,12 +55,18 @@ fun AnimatedMap(
),
markers = markers,
globeColors,
+ onClickRelayItemId = onClickRelayItemId,
+ onLongClickRelayItemId = onLongClickRelayItemId,
)
}
@Composable
-internal fun Map(modifier: Modifier = Modifier, mapViewState: MapViewState) {
-
+internal fun Map(
+ modifier: Modifier = Modifier,
+ mapViewState: MapViewState,
+ onClickRelayItemId: (GeoLocationId) -> Unit,
+ onLongClickRelayItemId: (Offset, GeoLocationId) -> Unit,
+) {
var view: MapGLSurfaceView? = remember { null }
val lifeCycleState = LocalLifecycleOwner.current.lifecycle
@@ -76,7 +92,25 @@ internal fun Map(modifier: Modifier = Modifier, mapViewState: MapViewState) {
}
}
- AndroidView(modifier = modifier, factory = { MapGLSurfaceView(it) }) { glSurfaceView ->
+ AndroidView(
+ modifier =
+ Modifier.pointerInput(lifeCycleState) {
+ detectTapGestures(
+ onTap = {
+ Logger.i("Registered marker click: $it")
+ val result = view?.onMapClick(it) ?: return@detectTapGestures
+ onClickRelayItemId(result.first)
+ },
+ onLongPress = {
+ Logger.i("Registered marker long click")
+ val result = view?.onMapClick(it) ?: return@detectTapGestures
+
+ onLongClickRelayItemId(result.second, result.first)
+ },
+ )
+ }.then(modifier),
+ factory = { MapGLSurfaceView(it) },
+ ) { glSurfaceView ->
view = glSurfaceView
glSurfaceView.setData(mapViewState)
}
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/CameraPosition.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/CameraPosition.kt
index b66b0ea657..e8ac13f793 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/CameraPosition.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/CameraPosition.kt
@@ -4,4 +4,13 @@ import androidx.compose.runtime.Immutable
import net.mullvad.mullvadvpn.lib.model.LatLong
@Immutable
-data class CameraPosition(val latLong: LatLong, val zoom: Float, val verticalBias: Float)
+data class CameraPosition(
+ val latLong: LatLong,
+ val zoom: Float,
+ val verticalBias: Float,
+ val fov: Float = DEFAULT_FIELD_OF_VIEW,
+) {
+ companion object {
+ const val DEFAULT_FIELD_OF_VIEW = 70f
+ }
+}
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/LocationMarkerColors.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/LocationMarkerColors.kt
index 4e0959912b..b5fc439e6e 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/LocationMarkerColors.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/LocationMarkerColors.kt
@@ -8,7 +8,7 @@ data class LocationMarkerColors(
val centerColor: Color,
val ringBorderColor: Color = Color.White,
val shadowColor: Color = Color.Black.copy(alpha = DEFAULT_SHADOW_ALPHA),
- val perimeterColors: Color = centerColor.copy(alpha = DEFAULT_PERIMETER_ALPHA),
+ val perimeterColors: Color? = centerColor.copy(alpha = DEFAULT_PERIMETER_ALPHA),
) {
companion object {
private const val DEFAULT_SHADOW_ALPHA = 0.55f
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/Marker.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/Marker.kt
index a7f25ec545..aae3bb991e 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/Marker.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/data/Marker.kt
@@ -1,6 +1,8 @@
package net.mullvad.mullvadvpn.lib.map.data
+import android.os.Parcelable
import androidx.compose.runtime.Immutable
+import net.mullvad.mullvadvpn.lib.model.GeoLocationId
import net.mullvad.mullvadvpn.lib.model.LatLong
@Immutable
@@ -8,6 +10,7 @@ data class Marker(
val latLong: LatLong,
val size: Float = DEFAULT_MARKER_SIZE,
val colors: LocationMarkerColors,
+ val id: GeoLocationId? = null,
) {
companion object {
private const val DEFAULT_MARKER_SIZE = 0.02f
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/Constants.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/Constants.kt
index a0aacc34c6..292abcf06a 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/Constants.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/Constants.kt
@@ -12,9 +12,9 @@ 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 +
// zoom out)
-internal const val SHORT_ANIMATION_CUTOFF_MILLIS = 1700
+const val SHORT_ANIMATION_CUTOFF_MILLIS = 1700
// Multiplier for the zoom out animation
-internal const val FAR_ANIMATION_MAX_ZOOM_MULTIPLIER = 1.30f
+const val FAR_ANIMATION_MAX_ZOOM_MULTIPLIER = 1.80f
// When in the far animation we reach the MAX_ZOOM_MULTIPLIER, value is between 0 and 1
-internal const val MAX_MULTIPLIER_PEAK_TIMING = .35f
+const val MAX_MULTIPLIER_PEAK_TIMING = .35f
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLRenderer.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLRenderer.kt
index b767d894b7..40ffc7c61e 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLRenderer.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLRenderer.kt
@@ -5,19 +5,34 @@ import android.opengl.GLES20
import android.opengl.GLSurfaceView
import android.opengl.Matrix
import androidx.collection.LruCache
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import co.touchlab.kermit.Logger
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
+import kotlin.math.pow
+import kotlin.math.sqrt
import kotlin.math.tan
import net.mullvad.mullvadvpn.lib.map.data.CameraPosition
import net.mullvad.mullvadvpn.lib.map.data.LocationMarkerColors
import net.mullvad.mullvadvpn.lib.map.data.MapViewState
+import net.mullvad.mullvadvpn.lib.map.data.Marker
import net.mullvad.mullvadvpn.lib.map.internal.shapes.Globe
import net.mullvad.mullvadvpn.lib.map.internal.shapes.LocationMarker
+import net.mullvad.mullvadvpn.lib.model.map.Ray
+import net.mullvad.mullvadvpn.lib.model.map.Sphere
+import net.mullvad.mullvadvpn.lib.model.map.Vector3
+import net.mullvad.mullvadvpn.lib.model.map.rotateAroundX
+import net.mullvad.mullvadvpn.lib.model.map.rotateAroundY
+import net.mullvad.mullvadvpn.lib.model.map.toLatLng
+import net.mullvad.mullvadvpn.lib.model.map.toVector3
import net.mullvad.mullvadvpn.lib.model.toRadians
internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.Renderer {
private lateinit var globe: Globe
+ private var viewPortSize: Size = Size(0f, 0f)
+ private val radius: Float = 1f
// Due to location markers themselves containing colors we cache them to avoid recreating them
// for every draw call.
@@ -34,6 +49,8 @@ internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.R
}
private lateinit var viewState: MapViewState
+ private val projectionMatrix = newIdentityMatrix()
+ private var globalViewMatrix = newIdentityMatrix()
override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
globe = Globe(resources)
@@ -51,8 +68,6 @@ internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.R
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)
}
- private val projectionMatrix = newIdentityMatrix()
-
override fun onDrawFrame(gl10: GL10) {
// Clear canvas
clear()
@@ -67,6 +82,7 @@ internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.R
Matrix.rotateM(viewMatrix, 0, viewState.cameraPosition.latLong.latitude.value, 1f, 0f, 0f)
Matrix.rotateM(viewMatrix, 0, viewState.cameraPosition.latLong.longitude.value, 0f, -1f, 0f)
+ globalViewMatrix = viewMatrix.copyOf()
globe.draw(projectionMatrix, viewMatrix, viewState.globeColors)
// Draw location markers
@@ -82,8 +98,9 @@ internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.R
private fun toOffsetY(cameraPosition: CameraPosition): Float {
val percent = cameraPosition.verticalBias
val z = cameraPosition.zoom - 1f
+
// Calculate the size of the plane at the current z position
- val planeSizeY = tan(FIELD_OF_VIEW.toRadians() / 2f) * z * 2f
+ val planeSizeY = tan(cameraPosition.fov.toRadians() / 2f) * z * 2f
// Calculate the start of the plane
val planeStartY = planeSizeY / 2f
@@ -105,6 +122,8 @@ internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.R
override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
+ viewPortSize = Size(width.toFloat(), height.toFloat())
+
val ratio: Float = width.toFloat() / height.toFloat()
if (ratio.isFinite()) {
@@ -121,6 +140,82 @@ internal class MapGLRenderer(private val resources: Resources) : GLSurfaceView.R
fun setViewState(viewState: MapViewState) {
this.viewState = viewState
+ markerVector = viewState.locationMarker.map { it.latLong.toVector3() to it }.toMap()
+ }
+
+ var markerVector = mapOf<Vector3, Marker>()
+
+ fun closestMarker(offset: Offset): Pair<Marker?, Float>? {
+ val cameraz = -viewState.cameraPosition.zoom
+ val camerax = 0f
+ val cameray = toOffsetY(viewState.cameraPosition)
+
+ val camera = Vector3(camerax, cameray, cameraz)
+
+ val sphere = Sphere(Vector3(0f, 0f, 0f), 1f)
+ val ratio: Float = viewPortSize.width.toFloat() / viewPortSize.height.toFloat()
+
+ val directionVector =
+ calculateDirectionVector(
+ viewState.cameraPosition.fov,
+ ratio,
+ viewPortSize.width,
+ viewPortSize.height,
+ offset.x,
+ offset.y,
+ nearPlaneDistance = PERSPECTIVE_Z_NEAR,
+ )
+
+ val ray = Ray(camera, directionVector)
+
+ val oc = ray.origin - sphere.center // Vector from ray origin to sphere center
+ val a = ray.direction.dot(ray.direction)
+ val b = 2f * oc.dot(ray.direction)
+ val c = oc.dot(oc) - sphere.radius.pow(2f)
+ val discriminant = b.pow(2f) - 4f * a * c
+
+ if (discriminant < 0f) {
+ return null // No intersection
+ } else {
+ val t = (-b - sqrt(discriminant)) / (2f * a) // Closest intersection point
+ val t2 = (-b + sqrt(discriminant)) / (2f * a) // Closest intersection point
+ Logger.d("Intersection t1: $t, t2: $t2")
+ val point2 = ray.origin + ray.direction * t2
+
+ val newPosition =
+ point2
+ .rotateAroundX(-viewState.cameraPosition.latLong.latitude.value)
+ .rotateAroundY(viewState.cameraPosition.latLong.longitude.value)
+
+ Logger.d("Intersection point2: $point2")
+ Logger.d("Intersection real vector: $newPosition")
+ Logger.d("Clicked lat lng: ${newPosition.toLatLng()}")
+
+ val closestMarker = markerVector.minByOrNull { it.key.distanceTo(newPosition) }
+
+ if (closestMarker != null) {
+ return closestMarker.value to closestMarker.key.distanceTo(newPosition)
+ }
+
+ return null
+ }
+ }
+
+ fun calculateDirectionVector(
+ fovy: Float,
+ aspectRatio: Float,
+ viewportWidth: Float,
+ viewportHeight: Float,
+ tapScreenX: Float,
+ tapScreenY: Float,
+ nearPlaneDistance: Float = 1.0f,
+ ): Vector3 {
+ val halfHeight = tan(fovy.toRadians() / 2.0f) * nearPlaneDistance
+ val halfWidth = halfHeight * aspectRatio
+ val x = (2.0f * tapScreenX / viewportWidth - 1.0f) * halfWidth
+ val y = (1.0f - 2.0f * tapScreenY / viewportHeight) * halfHeight
+ val z = -nearPlaneDistance
+ return Vector3(x, y, z).normalize()
}
companion object {
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLSurfaceView.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLSurfaceView.kt
index 19dd085524..3607cca820 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLSurfaceView.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/MapGLSurfaceView.kt
@@ -2,8 +2,11 @@ package net.mullvad.mullvadvpn.lib.map.internal
import android.content.Context
import android.opengl.GLSurfaceView
+import androidx.compose.ui.geometry.Offset
import net.mullvad.mullvadvpn.lib.map.BuildConfig
import net.mullvad.mullvadvpn.lib.map.data.MapViewState
+import net.mullvad.mullvadvpn.lib.model.GeoLocationId
+import net.mullvad.mullvadvpn.lib.model.RelayItemId
internal class MapGLSurfaceView(context: Context) : GLSurfaceView(context) {
@@ -28,4 +31,11 @@ internal class MapGLSurfaceView(context: Context) : GLSurfaceView(context) {
renderer.setViewState(viewState)
requestRender()
}
+
+ fun onMapClick(offset: Offset): Pair<GeoLocationId, Offset>? {
+ val (marker, distance) = renderer.closestMarker(offset) ?: return null
+ if (distance < 0.02f) {
+ return marker?.id?.let { it to offset }
+ } else return null
+ }
}
diff --git a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/shapes/LocationMarker.kt b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/shapes/LocationMarker.kt
index 26e69416b9..c12209273c 100644
--- a/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/shapes/LocationMarker.kt
+++ b/android/lib/map/src/main/kotlin/net/mullvad/mullvadvpn/lib/map/internal/shapes/LocationMarker.kt
@@ -54,14 +54,17 @@ internal class LocationMarker(val colors: LocationMarkerColors) {
val modelViewMatrix = viewMatrix.copyOf()
GLES20.glUseProgram(shaderProgram)
-
Matrix.rotateM(modelViewMatrix, 0, latLong.longitude.value, 0f, 1f, 0f)
Matrix.rotateM(modelViewMatrix, 0, latLong.latitude.value, -1f, 0f, 0f)
Matrix.scaleM(modelViewMatrix, 0, size, size, 1f)
// Translate marker to put it above the globe
- Matrix.translateM(modelViewMatrix, 0, 0f, 0f, MARKER_TRANSLATE_Z_FACTOR)
+ if (colors.perimeterColors != null) {
+ Matrix.translateM(modelViewMatrix, 0, 0f, 0f, MARKER_TRANSLATE_Z_FACTOR + 0.0003f)
+ } else {
+ Matrix.translateM(modelViewMatrix, 0, 0f, 0f, MARKER_TRANSLATE_Z_FACTOR)
+ }
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, positionBuffer)
GLES20.glVertexAttribPointer(
@@ -148,37 +151,53 @@ internal class LocationMarker(val colors: LocationMarkerColors) {
}
@Suppress("MagicNumber")
- private fun createRings(): List<Ring> =
- listOf(
- circleFanVertices(
- 32,
- 0.5f,
- floatArrayOf(0.0f, 0.0f, 0.0f),
- colors.perimeterColors,
- colors.perimeterColors,
- ), // Semi-transparent outer
+ private fun createRings(): List<Ring> = buildList {
+ colors.perimeterColors?.let {
+ // Semi-transparent outer
+ add(
+ circleFanVertices(
+ 32,
+ 0.5f,
+ floatArrayOf(0.0f, 0.0f, 0.0f),
+ colors.perimeterColors,
+ colors.perimeterColors,
+ )
+ )
+ }
+
+ // Shadow
+ add(
circleFanVertices(
16,
0.28f,
floatArrayOf(0.0f, -0.05f, 0.00001f),
colors.shadowColor,
colors.shadowColor.copy(alpha = 0.0f),
- ), // Shadow
+ )
+ )
+
+ // White ring
+ add(
circleFanVertices(
32,
0.185f,
floatArrayOf(0.0f, 0.0f, 0.00002f),
colors.ringBorderColor,
colors.ringBorderColor,
- ), // White ring
+ )
+ )
+
+ // Center colored circle
+ add(
circleFanVertices(
32,
0.15f,
floatArrayOf(0.0f, 0.0f, 0.00003f),
colors.centerColor,
colors.centerColor,
- ), // Center colored circle
+ )
)
+ }
fun onRemove() {
GLES20.glDeleteBuffers(2, intArrayOf(positionBuffer, colorBuffer), 0)