summaryrefslogtreecommitdiff
path: root/android-app/app/src
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-04-11 02:09:07 +0000
committerClaude <noreply@anthropic.com>2026-04-11 02:09:07 +0000
commita287abc937eb036271717e0867398fb68711c51e (patch)
treea7448ce36ea16e3ee0ce8ca4067869db3f528526 /android-app/app/src
parentfc7192288109fc3542670cbeeaebe0de2a75eb74 (diff)
Improve particle wind animation: longer lifetimes, wind-scaled count, zoom scatter
- MAX_AGE 8s → 25s: particles live much longer, drastically less twinkle - activeN scales 60→300 with wind speed (0–25 kt): calm = sparse drift, strong wind = dense streaks - Zoom-out detection: scatter() fires immediately when latRange grows >1.8×, instantly filling the new viewport instead of waiting for old particles to die - Alpha uses sine curve (sin(π·age/MAX_AGE)) instead of linear fade: particles ease in from birth, peak at mid-life, and ease out — no bright birth flash that caused the twinkle effect https://claude.ai/code/session_01HXPjBsogsJVRwCiekDGkJX
Diffstat (limited to 'android-app/app/src')
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt2
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/ui/map/ParticleWindView.kt20
2 files changed, 18 insertions, 4 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt b/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
index 996892e..e6355bd 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/MainActivity.kt
@@ -296,6 +296,8 @@ class MainActivity : AppCompatActivity(), SafetyFragment.SafetyListener {
private fun hideOverlays() {
fragmentContainer.visibility = View.GONE
fabRecordTrack.visibility = View.VISIBLE
+ // Re-apply cached conditions in case they arrived while the overlay was covering the sheet
+ lastConditions?.let { applyConditions(it) }
}
override fun onQuitRequested() {
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/map/ParticleWindView.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/map/ParticleWindView.kt
index 24b25db..26d9ea2 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/ui/map/ParticleWindView.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/map/ParticleWindView.kt
@@ -8,6 +8,7 @@ import android.util.AttributeSet
import android.view.View
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapLibreMap
+import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
import kotlin.random.Random
@@ -38,10 +39,12 @@ class ParticleWindView @JvmOverloads constructor(
private var windSpeedKt = 0.0
private val N = 300
+ private var activeN = 150 // wind-speed-dependent; updated in setWind()
private val particleLat = FloatArray(N)
private val particleLon = FloatArray(N)
private val particleAge = FloatArray(N)
- private val MAX_AGE = 8f // seconds before forced respawn
+ private val MAX_AGE = 25f // seconds before forced respawn
+ private var lastLatRange = 0f // for zoom-out scatter detection
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
strokeWidth = 3.5f
@@ -62,6 +65,8 @@ class ParticleWindView @JvmOverloads constructor(
fun setWind(dirFromDeg: Double, speedKt: Double) {
windDirFromDeg = dirFromDeg
windSpeedKt = speedKt
+ // Scale particle count: 60 at calm → 300 at 25+ kt
+ activeN = (60 + (speedKt.coerceIn(0.0, 25.0) / 25.0 * 240)).toInt()
}
// ── Rendering ────────────────────────────────────────────────────────────
@@ -86,6 +91,11 @@ class ParticleWindView @JvmOverloads constructor(
val latNorth = maxOf(nL.latitude, nR.latitude, fL.latitude, fR.latitude).toFloat()
val latRange = latNorth - latSouth
+ // If the user zoomed out significantly, immediately redistribute particles
+ // across the new viewport rather than waiting for them to drift to the edges.
+ if (lastLatRange > 0f && latRange > lastLatRange * 1.8f) scatter(m)
+ lastLatRange = latRange
+
// West edge = left screen side; east edge = right screen side.
// Using screen-ordered corners handles antimeridian crossing correctly.
val lonWest = minOf(nL.longitude, fL.longitude).toFloat()
@@ -109,7 +119,7 @@ class ParticleWindView @JvmOverloads constructor(
val tailDx = sin(screenRad).toFloat() * TAIL_PX
val tailDy = (-cos(screenRad)).toFloat() * TAIL_PX
- for (i in 0 until N) {
+ for (i in 0 until activeN) {
particleLat[i] += dlat
particleLon[i] += dlon
// Wrap longitude into [-180, 180] after movement
@@ -137,7 +147,9 @@ class ParticleWindView @JvmOverloads constructor(
LatLng(particleLat[i].toDouble(), particleLon[i].toDouble())
)
- val alpha = ((1f - particleAge[i] / MAX_AGE) * 220).toInt().coerceIn(40, 220)
+ // Sine curve: smooth fade-in from birth, peak at mid-life, smooth fade-out.
+ // No bright birth flash — particles ease in and ease out.
+ val alpha = (sin(PI * particleAge[i] / MAX_AGE) * 200).toInt().coerceIn(15, 200)
paint.color = Color.argb(alpha, 30, 100, 255)
canvas.drawLine(pt.x - tailDx, pt.y - tailDy, pt.x, pt.y, paint)
@@ -170,7 +182,7 @@ class ParticleWindView @JvmOverloads constructor(
val lonEast = maxOf(nR.longitude, fR.longitude).toFloat()
val lonSpan = if (lonEast >= lonWest) lonEast - lonWest else lonEast - lonWest + 360f
- for (i in 0 until N) {
+ for (i in 0 until activeN) {
particleLat[i] = latSouth + Random.nextFloat() * latRange
var newLon = lonWest + Random.nextFloat() * lonSpan
if (newLon > 180f) newLon -= 360f