diff --git a/app/src/androidTest/java/com/rtbishop/look4sat/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/rtbishop/look4sat/ExampleInstrumentedTest.kt
index f99ae446..27b6a311 100644
--- a/app/src/androidTest/java/com/rtbishop/look4sat/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/rtbishop/look4sat/ExampleInstrumentedTest.kt
@@ -12,6 +12,6 @@ class ExampleInstrumentedTest {
@Test
fun useAppContext() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.rtbishop.bbctestapp", appContext.packageName)
+ assertEquals("com.rtbishop.look4sat", appContext.packageName)
}
}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarScreen.kt
index cbc26b19..b8422118 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarScreen.kt
@@ -9,19 +9,16 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import androidx.compose.ui.viewinterop.AndroidView
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.model.SatRadio
-import timber.log.Timber
@Composable
fun RadarScreen(navController: NavController) {
@@ -65,19 +62,6 @@ fun RadarScreen(navController: NavController) {
ElevatedCard(modifier = Modifier
.fillMaxSize()
.weight(1f)) {
-// val context = LocalContext.current
-// AndroidView({ RadarView(context) }, modifier = Modifier.fillMaxSize()) { radarView ->
-// Timber.d("Radar view recomposition")
-// radarView.setShowAim(true)
-// radarView.setScanning(true)
-// radarData.value?.let {
-// radarView.setPosition(it.satPos)
-// radarView.setPositions(it.satTrack)
-// }
-// orientation.value?.let {
-// radarView.setOrientation(it.first, it.second, it.first)
-// }
-// }
radarData.value?.let { data ->
orientation.value?.let { triple ->
RadarViewCompose(
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarView.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarView.kt
index a78a0e84..767ec160 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarView.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarView.kt
@@ -1,265 +1,142 @@
-/*
- * Look4Sat. Amateur radio satellite tracker and pass predictor.
- * Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
+@file:OptIn(ExperimentalTextApi::class)
+
package com.rtbishop.look4sat.presentation.radarScreen
-import android.content.Context
-import android.graphics.*
-import android.view.View
-import androidx.core.content.ContextCompat
-import com.rtbishop.look4sat.R
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.*
+import androidx.compose.ui.graphics.drawscope.*
+import androidx.compose.ui.text.*
+import androidx.compose.ui.unit.sp
import com.rtbishop.look4sat.domain.predict.PI_2
import com.rtbishop.look4sat.domain.predict.SatPos
import com.rtbishop.look4sat.domain.predict.TWO_PI
import com.rtbishop.look4sat.utility.toRadians
import kotlin.math.cos
-import kotlin.math.min
import kotlin.math.sin
-class RadarView(context: Context) : View(context) {
-
- private val defaultColor = ContextCompat.getColor(context, R.color.accent)
- private val scale = resources.displayMetrics.density
- private val strokeSize = scale * 2f
- private var position: SatPos? = null
- private var positions: List = emptyList()
-
- private var radarColor = ContextCompat.getColor(context, R.color.textMain)
- private var radarCircleNum = 3
- private var radarPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = radarColor
- style = Paint.Style.STROKE
- strokeWidth = strokeSize
- }
-
- private var shouldShowSweep = true
- private var sweepColor = defaultColor
- private var sweepDegrees = 0f
- private var sweepSpeed = 15.0f
- private var sweepPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = sweepColor
- }
-
- private var shouldShowBeacons = true
- private var beaconColor = defaultColor
- private var beaconSize = scale * 7f
- private var beaconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = beaconColor
- style = Paint.Style.FILL
- }
-
- private var radarTextSize = scale * 16f
- private var radarTextColor = defaultColor
- private var radarTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = radarTextColor
- textSize = radarTextSize
- }
-
- private var isTrackCreated = false
- private val trackPath: Path = Path()
- private var trackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = Color.RED
- style = Paint.Style.STROKE
- strokeWidth = strokeSize
- }
-
- private val arrowPath = Path()
- private var arrowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = ContextCompat.getColor(context, R.color.accent)
- style = Paint.Style.FILL
- strokeWidth = strokeSize
- }
-
- private var shouldShowAim = true
- private var aimColor = Color.RED
- private var aimPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = aimColor
- style = Paint.Style.STROKE
- strokeWidth = strokeSize
- }
-
- private var shouldRotateRadar = true
- private var azimuth: Float = 0f
- private var pitch: Float = 0f
- private var roll: Float = 0f
-
- fun setScanning(isScanning: Boolean) {
- shouldShowSweep = isScanning
- }
-
- fun setPosition(position: SatPos) {
- this.position = position
- }
-
- fun setPositions(positions: List) {
- this.positions = positions
- }
-
- fun setShowAim(showAim: Boolean) {
- shouldShowAim = showAim
- }
-
- fun setOrientation(azimuth: Float, pitch: Float, roll: Float) {
- this.azimuth = azimuth
- this.pitch = pitch
- this.roll = roll
- if (shouldRotateRadar) {
- this.rotation = -azimuth
+@Composable
+fun RadarViewCompose(item: SatPos, items: List, orientation: Triple) {
+ val radarColor = Color(0xFFDCDCDC)
+ val primaryColor = Color(0xFFFFE082)
+ val secondaryColor = Color(0xFFDC0000)
+ val strokeWidth = 4f
+ val measurer = rememberTextMeasurer()
+ val sweepDegrees = remember { mutableStateOf(0f) }
+ val trackCreated = remember { mutableStateOf(false) }
+ val trackPath = remember { mutableStateOf(Path()) }
+ val trackEffect = remember { mutableStateOf(PathEffect.cornerPathEffect(0f)) }
+ Canvas(modifier = Modifier.fillMaxSize()) {
+ val radius = (size.minDimension / 2f) * 0.95f
+ if (!trackCreated.value) {
+ trackPath.value = createTrackPath(items, radius)
+ trackEffect.value = createTrackEffect(trackPath.value)
+ trackCreated.value = true
}
- }
-
- override fun onDraw(canvas: Canvas) {
- val radarWidth = width - paddingLeft - paddingRight
- val radarHeight = height - paddingTop - paddingBottom
- val radarRadius = min(width, height) * 0.48f
- val cx = paddingLeft + radarWidth / 2f
- val cy = paddingTop + radarHeight / 2f
-
- if (positions.isNotEmpty() && !isTrackCreated) {
- createPassTrajectory(radarRadius)
- createPassTrajectoryArrow()
- isTrackCreated = true
- }
-
- canvas.drawColor(ContextCompat.getColor(context, R.color.cardRegular))
- drawRadarCircle(canvas, cx, cy, radarRadius)
- drawRadarCross(canvas, cx, cy, radarRadius)
- drawRadarText(canvas, cx, radarRadius)
-
- canvas.translate(cx, cy)
- if (positions.isNotEmpty()) {
- canvas.drawPath(trackPath, trackPaint)
- canvas.drawPath(trackPath, arrowPaint)
- }
- if (shouldShowBeacons) {
- drawSatellite(canvas, radarRadius)
- }
- if (shouldShowAim) {
- drawCrosshair(canvas, azimuth, pitch, radarRadius)
- }
- if (shouldShowSweep) {
- canvas.translate(-cx, -cy)
- drawRadarSweep(canvas, cx, cy, radarRadius)
- sweepDegrees = (sweepDegrees + 360 / sweepSpeed / 60) % 360
- }
- invalidate()
- }
-
- private fun drawRadarCircle(canvas: Canvas, cx: Float, cy: Float, radius: Float) {
- for (i in 0 until radarCircleNum) {
- canvas.drawCircle(cx, cy, radius - radius / radarCircleNum * i, radarPaint)
- }
- }
-
- private fun drawRadarCross(canvas: Canvas, cx: Float, cy: Float, radius: Float) {
- canvas.drawLine(cx - radius, cy, cx + radius, cy, radarPaint)
- canvas.drawLine(cx, cy - radius, cx, cy + radius, radarPaint)
- }
-
- private fun drawRadarText(canvas: Canvas, cx: Float, radius: Float) {
- for (i in 0 until radarCircleNum) {
- val degText = " ${(90 / radarCircleNum) * (radarCircleNum - i)}°"
- canvas.drawText(degText, cx, radius - radius / radarCircleNum * i, radarTextPaint)
- }
- }
-
- private fun drawSatellite(canvas: Canvas, radarRadius: Float) {
- position?.let { satPos ->
- if (satPos.elevation > 0) {
- val satX = sph2CartX(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
- val satY = sph2CartY(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
- canvas.drawCircle(satX, -satY, beaconSize, beaconPaint)
+ rotate(-orientation.first) {
+ drawSweep(center, sweepDegrees.value, radius, primaryColor)
+ drawRadar(radius, radarColor, strokeWidth, 3)
+ drawInfo(radius, primaryColor, measurer, 3)
+ translate(center.x, center.y) {
+ drawTrack(trackPath.value, trackEffect.value, secondaryColor, primaryColor)
+ if (item.elevation > 0) drawPosition(item, radius, primaryColor)
+ drawAim(orientation.first, orientation.second, radius, strokeWidth, secondaryColor)
}
+ sweepDegrees.value = (sweepDegrees.value + 360 / 12.0f / 60) % 360
}
}
-
- private fun drawCrosshair(canvas: Canvas, azimuth: Float, pitch: Float, radarRadius: Float) {
- val azimuthRad = azimuth.toDouble().toRadians()
- val tmpElevation = pitch.toDouble().toRadians()
- val elevationRad = if (tmpElevation > 0.0) 0.0 else tmpElevation
- val crossX = sph2CartX(azimuthRad, -elevationRad, radarRadius.toDouble())
- val crossY = sph2CartY(azimuthRad, -elevationRad, radarRadius.toDouble())
- canvas.drawLine(crossX - radarTextSize, -crossY, crossX + radarTextSize, -crossY, aimPaint)
- canvas.drawLine(crossX, -crossY - radarTextSize, crossX, -crossY + radarTextSize, aimPaint)
- canvas.drawCircle(crossX, -crossY, radarTextSize / 2, aimPaint)
- }
-
- private fun sph2CartX(azimuth: Double, elevation: Double, r: Double): Float {
- val radius = r * (PI_2 - elevation) / PI_2
- return (radius * cos(PI_2 - azimuth)).toFloat()
- }
-
- private fun sph2CartY(azimuth: Double, elevation: Double, r: Double): Float {
- val radius = r * (PI_2 - elevation) / PI_2
- return (radius * sin(PI_2 - azimuth)).toFloat()
- }
-
- private fun createPassTrajectory(radarRadius: Float) {
- positions.forEachIndexed { index, satPos ->
- val passX = sph2CartX(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
- val passY = sph2CartY(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
- if (index == 0) {
- trackPath.moveTo(passX, -passY)
- } else {
- trackPath.lineTo(passX, -passY)
- }
- }
- }
-
- private fun createPassTrajectoryArrow() {
- val radius = beaconSize
- val sides = 3
- val angle = TWO_PI / sides
- arrowPath.moveTo((radius * cos(angle)).toFloat(), (radius * sin(angle)).toFloat())
- for (i in 1 until sides) {
- val x = (radius * cos(angle - angle * i)).toFloat()
- val y = (radius * sin(angle - angle * i)).toFloat()
- arrowPath.lineTo(x, y)
- }
- arrowPath.close()
- val trackLength = PathMeasure(trackPath, false).length
- val quarter = trackLength / 4f
- val center = trackLength / 2f
- val effect = PathDashPathEffect(arrowPath, center, quarter, PathDashPathEffect.Style.ROTATE)
- arrowPaint.pathEffect = effect
- }
-
- private fun drawRadarSweep(canvas: Canvas, cx: Float, cy: Float, radius: Float) {
- val sweepGradient = SweepGradient(
- cx,
- cy,
- intArrayOf(
- Color.TRANSPARENT,
- changeAlpha(sweepColor, 0),
- changeAlpha(sweepColor, 164),
- changeAlpha(sweepColor, 255),
- changeAlpha(sweepColor, 255)
- ),
- floatArrayOf(0.0f, 0.55f, 0.996f, 0.999f, 1f)
- )
- sweepPaint.shader = sweepGradient
- canvas.rotate(-90 + sweepDegrees, cx, cy)
- canvas.drawCircle(cx, cy, radius, sweepPaint)
- }
-
- private fun changeAlpha(color: Int, alpha: Int): Int {
- val red = Color.red(color)
- val green = Color.green(color)
- val blue = Color.blue(color)
- return Color.argb(alpha, red, green, blue)
- }
+}
+
+private fun DrawScope.drawRadar(radius: Float, color: Color, width: Float, circles: Int) {
+ for (i in 0 until circles) {
+ val circleRadius = radius - radius / circles.toFloat() * i.toFloat()
+ drawCircle(color, circleRadius, style = Stroke(width))
+ }
+ drawLine(color, center.copy(x = center.x - radius), center.copy(x = center.x + radius), width)
+ drawLine(color, center.copy(y = center.y - radius), center.copy(y = center.y + radius), width)
+}
+
+private fun DrawScope.drawInfo(radius: Float, color: Color, measurer: TextMeasurer, circles: Int) {
+ for (i in 0 until circles) {
+ val textY = (radius - radius / circles * i) - 32f
+ val textDeg = " ${(90 / circles) * (circles - i)}°"
+ drawText(measurer, textDeg, center.copy(y = textY), style = TextStyle(color, 15.sp))
+ }
+}
+
+private fun DrawScope.drawTrack(path: Path, effect: PathEffect, color: Color, effectColor: Color) {
+ drawPath(path, color, style = Stroke(4f))
+ drawPath(path, effectColor, style = Stroke(pathEffect = effect))
+}
+
+private fun DrawScope.drawPosition(item: SatPos, radius: Float, color: Color) {
+ val satX = sph2CartX(item.azimuth, item.elevation, radius.toDouble())
+ val satY = sph2CartY(item.azimuth, item.elevation, radius.toDouble())
+ drawCircle(color, 18f, center.copy(satX, -satY))
+}
+
+private fun DrawScope.drawAim(azim: Float, elev: Float, radius: Float, width: Float, color: Color) {
+ val size = 36f
+ val azimRadians = azim.toDouble().toRadians()
+ val tempElevRadians = elev.toDouble().toRadians()
+ val elevRadians = if (tempElevRadians > 0.0) 0.0 else tempElevRadians
+ val aimX = sph2CartX(azimRadians, -elevRadians, radius.toDouble())
+ val aimY = sph2CartY(azimRadians, -elevRadians, radius.toDouble())
+ try {
+ drawLine(color, center.copy(aimX - size, -aimY), center.copy(aimX + size, -aimY), width)
+ drawLine(color, center.copy(aimX, -aimY - size), center.copy(aimX, -aimY + size), width)
+ drawCircle(color, size / 2, center.copy(aimX, -aimY), style = Stroke(width))
+ } catch (exception: Exception) {
+// Timber.d(exception)
+ }
+}
+
+private fun DrawScope.drawSweep(center: Offset, degrees: Float, radius: Float, color: Color) {
+ val colors = listOf(Color.Transparent, Color(0x80FFE082), color)
+ val colorStops = listOf(0.64f, 0.995f, 1f)
+ val brush = ShaderBrush(SweepGradientShader(center, colors, colorStops))
+ rotate(-90 + degrees, center) { drawCircle(brush, radius, style = Fill) }
+}
+
+private fun createTrackPath(positions: List, radius: Float): Path {
+ val trackPath = Path()
+ positions.forEachIndexed { index, satPos ->
+ val passX = sph2CartX(satPos.azimuth, satPos.elevation, radius.toDouble())
+ val passY = sph2CartY(satPos.azimuth, satPos.elevation, radius.toDouble())
+ if (index == 0) trackPath.moveTo(passX, -passY) else trackPath.lineTo(passX, -passY)
+ }
+ return trackPath
+}
+
+private fun createTrackEffect(trackPath: Path): PathEffect {
+ val shape = Path()
+ val shapeSides = 3
+ val shapeRadius = 18f
+ val angle = TWO_PI / shapeSides
+ shape.moveTo((shapeRadius * cos(angle)).toFloat(), (shapeRadius * sin(angle)).toFloat())
+ for (i in 1 until shapeSides) {
+ val x = (shapeRadius * cos(angle - angle * i)).toFloat()
+ val y = (shapeRadius * sin(angle - angle * i)).toFloat()
+ shape.lineTo(x, y)
+ }
+ shape.close()
+ val trackLength = PathMeasure().apply { setPath(trackPath, false) }.length
+ val advance = trackLength / 2f
+ val phase = trackLength / 4f
+ return PathEffect.stampedPathEffect(shape, advance, phase, StampedPathEffectStyle.Rotate)
+}
+
+private fun sph2CartX(azim: Double, elev: Double, r: Double): Float {
+ val radius = r * (PI_2 - elev) / PI_2
+ return (radius * cos(PI_2 - azim)).toFloat()
+}
+
+private fun sph2CartY(azim: Double, elev: Double, r: Double): Float {
+ val radius = r * (PI_2 - elev) / PI_2
+ return (radius * sin(PI_2 - azim)).toFloat()
}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarViewCompose.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarViewCompose.kt
deleted file mode 100644
index 4cff8156..00000000
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarViewCompose.kt
+++ /dev/null
@@ -1,155 +0,0 @@
-@file:OptIn(ExperimentalTextApi::class)
-
-package com.rtbishop.look4sat.presentation.radarScreen
-
-import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.*
-import androidx.compose.ui.graphics.drawscope.*
-import androidx.compose.ui.text.*
-import androidx.compose.ui.unit.sp
-import com.rtbishop.look4sat.domain.predict.PI_2
-import com.rtbishop.look4sat.domain.predict.SatPos
-import com.rtbishop.look4sat.domain.predict.TWO_PI
-import com.rtbishop.look4sat.utility.toRadians
-import kotlin.math.cos
-import kotlin.math.sin
-
-@Composable
-fun RadarViewCompose(item: SatPos, items: List, orientation: Triple) {
- val measurer = rememberTextMeasurer()
- val newTrackPath = remember { mutableStateOf(Path()) }
- val newArrowPath = remember { mutableStateOf(Path()) }
- var isTrackCreated = false
- val yellowColor = Color(0xFFFFE082)
- val whiteColor = Color(0xCCFFFFFF)
- val redColor = Color(0xFFB71C1C)
- val strokeWidth = 4f
- Canvas(modifier = Modifier.fillMaxSize()) {
- val radius = (size.minDimension / 2f) * 0.95f
- if (items.isNotEmpty() && !isTrackCreated) {
- newTrackPath.value = createPassTrajectory(items, radius)
- newArrowPath.value = createPassTrajectoryArrow()
- isTrackCreated = true
- }
- rotate(-orientation.first) {
- drawRadarCirle(radius, whiteColor, strokeWidth)
- drawRadarCross(radius, whiteColor, strokeWidth)
- drawRadarText(radius, yellowColor, measurer)
- translate(center.x, center.y) {
- if (items.isNotEmpty()) {
- val effect = createPathEffect(newTrackPath.value, newArrowPath.value)
- drawPath(newTrackPath.value, redColor, style = Stroke(4f))
- drawPath(newTrackPath.value, yellowColor, style = Stroke(pathEffect = effect))
- }
- drawSatellite(item, radius, yellowColor)
- drawCrosshair(orientation.first, orientation.second, radius, strokeWidth)
- }
- }
- }
-}
-
-private fun DrawScope.drawRadarCirle(radius: Float, color: Color, width: Float, circles: Int = 3) {
- for (i in 0 until circles) {
- val circleRadius = radius - radius / circles.toFloat() * i.toFloat()
- drawCircle(color, circleRadius, style = Stroke(width))
- }
-}
-
-private fun DrawScope.drawRadarCross(radius: Float, color: Color, width: Float) {
- drawLine(color, center, center.copy(x = center.x - radius), strokeWidth = width)
- drawLine(color, center, center.copy(x = center.x + radius), strokeWidth = width)
- drawLine(color, center, center.copy(y = center.y - radius), strokeWidth = width)
- drawLine(color, center, center.copy(y = center.y + radius), strokeWidth = width)
-}
-
-private fun DrawScope.drawRadarText(
- radius: Float, color: Color, measurer: TextMeasurer, circles: Int = 3
-) {
- for (i in 0..circles) {
- val textY = (radius - radius / circles * i) + 25f
- val textDeg = " ${(90 / circles) * (circles - i)}°"
- drawText(measurer, textDeg, center.copy(y = textY), style = TextStyle(color, 16.sp))
- }
-}
-
-private fun DrawScope.drawSatellite(item: SatPos, radius: Float, color: Color) {
- if (item.elevation > 0) {
- val satX = sph2CartX(item.azimuth, item.elevation, radius.toDouble())
- val satY = sph2CartY(item.azimuth, item.elevation, radius.toDouble())
- drawCircle(color, 16f, center.copy(satX, -satY))
- }
-}
-
-private fun DrawScope.drawCrosshair(azimuth: Float, elevation: Float, radius: Float, width: Float) {
- val redColor = Color(0xFFFF0000)
- val size = 36f
- val azimuthRad = azimuth.toDouble().toRadians()
- val tmpElevation = elevation.toDouble().toRadians()
- val elevationRad = if (tmpElevation > 0.0) 0.0 else tmpElevation
- val crossX = sph2CartX(azimuthRad, -elevationRad, radius.toDouble())
- val crossY = sph2CartY(azimuthRad, -elevationRad, radius.toDouble())
- drawLine(
- redColor,
- center.copy(crossX - size, -crossY),
- center.copy(crossX + size, -crossY),
- strokeWidth = width
- )
- drawLine(
- redColor,
- center.copy(crossX, -crossY - size),
- center.copy(crossX, -crossY + size),
- strokeWidth = width
- )
- drawCircle(redColor, size / 2, center.copy(crossX, -crossY), style = Stroke(width))
-}
-
-private fun createPassTrajectory(positions: List, radarRadius: Float): Path {
- val trackPath = Path()
- positions.forEachIndexed { index, satPos ->
- val passX = sph2CartX(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
- val passY = sph2CartY(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
- if (index == 0) {
- trackPath.moveTo(passX, -passY)
- } else {
- trackPath.lineTo(passX, -passY)
- }
- }
- return trackPath
-}
-
-private fun createPassTrajectoryArrow(): Path {
- val arrowPath = Path()
- val radius = 24f
- val sides = 3
- val angle = TWO_PI / sides
- arrowPath.moveTo((radius * cos(angle)).toFloat(), (radius * sin(angle)).toFloat())
- for (i in 1 until sides) {
- val x = (radius * cos(angle - angle * i)).toFloat()
- val y = (radius * sin(angle - angle * i)).toFloat()
- arrowPath.lineTo(x, y)
- }
- arrowPath.close()
- return arrowPath
-}
-
-private fun createPathEffect(trackPath: Path, arrowPath: Path): PathEffect {
- val trackLength = PathMeasure().apply { setPath(trackPath, false) }.length
- val quarter = trackLength / 4f
- val center = trackLength / 2f
- return PathEffect.stampedPathEffect(arrowPath, center, quarter, StampedPathEffectStyle.Rotate)
-}
-
-private fun sph2CartX(azimuth: Double, elevation: Double, r: Double): Float {
- val radius = r * (PI_2 - elevation) / PI_2
- return (radius * cos(PI_2 - azimuth)).toFloat()
-}
-
-private fun sph2CartY(azimuth: Double, elevation: Double, r: Double): Float {
- val radius = r * (PI_2 - elevation) / PI_2
- return (radius * sin(PI_2 - azimuth)).toFloat()
-}