mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
feat: bidirectional drag sync between elevation curve and radar track
- ElevationCurveChart is now controlled (progress + onProgressChange) - MutualRadarView shows shared time-cursor positions for both stations and is draggable/tappable to move the cursor - MutualPassCard owns a single dragProgress feeding both charts, so dragging either chart moves the other in sync
This commit is contained in:
1 parent
4ca33c37ad
commit
28fa373db0
3 files changed
+51
-11
No files matched your search
+9
-10
@@ -30,10 +30,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
@@ -50,6 +47,8 @@ import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Draggable dual-station elevation curve chart.
|
||||
* Controlled component: [progress] (0..1) and [onProgressChange] are owned by the parent,
|
||||
* so the same time cursor can be shared with the radar track view.
|
||||
*/
|
||||
@Composable
|
||||
fun ElevationCurveChart(
|
||||
@@ -57,6 +56,8 @@ fun ElevationCurveChart(
|
||||
startTime: Long,
|
||||
endTime: Long,
|
||||
maxElev: Double,
|
||||
progress: Float = 0.5f,
|
||||
onProgressChange: (Float) -> Unit = {},
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
if (samples.isEmpty()) return
|
||||
@@ -73,8 +74,6 @@ fun ElevationCurveChart(
|
||||
val gridColorArgb = gridColor.toArgb()
|
||||
val timeFormat = remember { SimpleDateFormat("HH:mm", Locale.getDefault()) }
|
||||
|
||||
var dragProgress by remember { mutableFloatStateOf(0.5f) }
|
||||
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
@@ -83,12 +82,12 @@ fun ElevationCurveChart(
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f))
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures { offset ->
|
||||
dragProgress = (offset.x / size.width.toFloat()).coerceIn(0f, 1f)
|
||||
onProgressChange((offset.x / size.width.toFloat()).coerceIn(0f, 1f))
|
||||
}
|
||||
}
|
||||
.pointerInput(Unit) {
|
||||
detectDragGestures { _, dragAmount ->
|
||||
dragProgress = (dragProgress + dragAmount.x / size.width.toFloat()).coerceIn(0f, 1f)
|
||||
detectDragGestures { change, _ ->
|
||||
onProgressChange((change.position.x / size.width.toFloat()).coerceIn(0f, 1f))
|
||||
}
|
||||
}
|
||||
) {
|
||||
@@ -180,8 +179,8 @@ fun ElevationCurveChart(
|
||||
drawPath(pathB, colorB, style = Stroke(width = 2.5f))
|
||||
|
||||
// Drag indicator
|
||||
val dragX = plotLeft + dragProgress * plotWidth
|
||||
val dragTime = startTime + (dragProgress * (endTime - startTime)).toLong()
|
||||
val dragX = plotLeft + progress * plotWidth
|
||||
val dragTime = startTime + (progress * (endTime - startTime)).toLong()
|
||||
|
||||
drawLine(
|
||||
onSurfaceColor,
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package com.rtbishop.look4sat.feature.mutual
|
||||
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.gestures.detectDragGestures
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -34,6 +36,7 @@ import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.PathEffect
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.text.TextMeasurer
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.drawText
|
||||
@@ -50,10 +53,14 @@ private const val STROKE_WIDTH = 2.5f
|
||||
/**
|
||||
* Polar radar chart showing the satellite track as seen from both stations
|
||||
* on one plot. Station A is drawn solid, station B dashed.
|
||||
* Controlled: [progress] (0..1) and [onProgressChange] are owned by the parent
|
||||
* so the time cursor can be shared with the elevation curve chart.
|
||||
*/
|
||||
@Composable
|
||||
fun MutualRadarView(
|
||||
trackSamples: List<TrackSample>,
|
||||
progress: Float = 0.5f,
|
||||
onProgressChange: (Float) -> Unit = {},
|
||||
labelA: String = "站点A",
|
||||
labelB: String = "站点B",
|
||||
modifier: Modifier = Modifier
|
||||
@@ -66,12 +73,29 @@ fun MutualRadarView(
|
||||
val textColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
val measurer = rememberTextMeasurer()
|
||||
|
||||
// Current-time samples (both stations' position at the shared cursor)
|
||||
val t0 = trackSamples.first().time
|
||||
val t1 = trackSamples.last().time
|
||||
val selectedTime = t0 + ((t1 - t0) * progress).toLong()
|
||||
val visibleSamples = trackSamples.filter { it.time <= selectedTime }
|
||||
val currentSample = visibleSamples.lastOrNull() ?: trackSamples.first()
|
||||
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
.padding(4.dp)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures { offset ->
|
||||
onProgressChange((offset.x / size.width.toFloat()).coerceIn(0f, 1f))
|
||||
}
|
||||
}
|
||||
.pointerInput(Unit) {
|
||||
detectDragGestures { change, _ ->
|
||||
onProgressChange((change.position.x / size.width.toFloat()).coerceIn(0f, 1f))
|
||||
}
|
||||
}
|
||||
) {
|
||||
val radius = size.minDimension / 2f * 0.92f
|
||||
val center = Offset(size.width / 2f, size.height / 2f)
|
||||
@@ -125,6 +149,14 @@ fun MutualRadarView(
|
||||
if (last.elevationA > 0f) drawCircle(colorA, 5f, losA)
|
||||
if (last.elevationB > 0f) drawCircle(colorB, 5f, losB)
|
||||
|
||||
// Shared time-cursor positions (both stations at selectedTime)
|
||||
val cursorA = sph2Cart(center, currentSample.azimuthA, currentSample.elevationA, radius)
|
||||
val cursorB = sph2Cart(center, currentSample.azimuthB, currentSample.elevationB, radius)
|
||||
drawCircle(colorA, 14f, cursorA, style = Stroke(3f))
|
||||
drawCircle(colorA, 6f, cursorA)
|
||||
drawCircle(colorB, 14f, cursorB, style = Stroke(3f))
|
||||
drawCircle(colorB, 6f, cursorB)
|
||||
|
||||
// Center dot
|
||||
drawCircle(gridColor, 4f, center)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@@ -361,6 +363,9 @@ private fun MutualPassCard(
|
||||
onClick: () -> Unit,
|
||||
onNavigateToRadar: () -> Unit
|
||||
) {
|
||||
// Shared time cursor: both the elevation curve and the radar track view
|
||||
// are controlled by this single progress value for bidirectional drag sync.
|
||||
var dragProgress by remember(pass) { mutableFloatStateOf(0.5f) }
|
||||
ElevatedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -419,6 +424,8 @@ private fun MutualPassCard(
|
||||
)
|
||||
MutualRadarView(
|
||||
trackSamples = pass.trackSamples,
|
||||
progress = dragProgress,
|
||||
onProgressChange = { dragProgress = it },
|
||||
labelA = "你",
|
||||
labelB = "友台"
|
||||
)
|
||||
@@ -429,7 +436,9 @@ private fun MutualPassCard(
|
||||
samples = pass.elevationSamples,
|
||||
startTime = pass.startTime,
|
||||
endTime = pass.endTime,
|
||||
maxElev = maxOf(pass.maxElevationA, pass.maxElevationB, 10.0)
|
||||
maxElev = maxOf(pass.maxElevationA, pass.maxElevationB, 10.0),
|
||||
progress = dragProgress,
|
||||
onProgressChange = { dragProgress = it }
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
Reference in new issue
Block a user