diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt index 725999660..4ee4b0828 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt @@ -226,7 +226,7 @@ fun DeviceMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigat ) } -@Suppress("LongMethod") +@Suppress("LongMethod", "CyclomaticComplexMethod") @Composable private fun DeviceMetricsChart( modifier: Modifier = Modifier, @@ -258,59 +258,82 @@ private fun DeviceMetricsChart( }, ) - LaunchedEffect(telemetries) { + val batteryData = remember(telemetries) { telemetries.filter { it.device_metrics?.battery_level != null } } + val chUtilData = remember(telemetries) { telemetries.filter { it.device_metrics?.channel_utilization != null } } + val airUtilData = remember(telemetries) { telemetries.filter { it.device_metrics?.air_util_tx != null } } + val voltageData = remember(telemetries) { telemetries.filter { it.device_metrics?.voltage != null } } + + val batteryStyle = + if (batteryData.isNotEmpty()) { + ChartStyling.createBoldLine(batteryColor, ChartStyling.MEDIUM_POINT_SIZE_DP) + } else { + null + } + val chUtilStyle = + if (chUtilData.isNotEmpty()) { + ChartStyling.createPointOnlyLine(chUtilColor, ChartStyling.LARGE_POINT_SIZE_DP) + } else { + null + } + val airUtilStyle = + if (airUtilData.isNotEmpty()) { + ChartStyling.createPointOnlyLine(airUtilColor, ChartStyling.LARGE_POINT_SIZE_DP) + } else { + null + } + + val leftLayerSeriesStyles = + remember(batteryStyle, chUtilStyle, airUtilStyle) { listOfNotNull(batteryStyle, chUtilStyle, airUtilStyle) } + + LaunchedEffect(batteryData, chUtilData, airUtilData, voltageData, leftLayerSeriesStyles) { modelProducer.runTransaction { /* Series for Left Axis (0-100%) */ - lineSeries { - series( - x = telemetries.map { it.time ?: 0 }, - y = telemetries.map { it.device_metrics?.battery_level ?: 0 }, - ) - val chUtilData = telemetries.filter { it.device_metrics?.channel_utilization != null } - series( - x = chUtilData.map { it.time ?: 0 }, - y = chUtilData.map { it.device_metrics?.channel_utilization ?: 0f }, - ) - val airUtilData = telemetries.filter { it.device_metrics?.air_util_tx != null } - series( - x = airUtilData.map { it.time ?: 0 }, - y = airUtilData.map { it.device_metrics?.air_util_tx ?: 0f }, - ) + if (leftLayerSeriesStyles.isNotEmpty()) { + lineSeries { + if (batteryData.isNotEmpty()) { + series( + x = batteryData.map { it.time ?: 0 }, + y = batteryData.map { (it.device_metrics?.battery_level ?: 0).toFloat() }, + ) + } + if (chUtilData.isNotEmpty()) { + series( + x = chUtilData.map { it.time ?: 0 }, + y = chUtilData.map { it.device_metrics?.channel_utilization ?: 0f }, + ) + } + if (airUtilData.isNotEmpty()) { + series( + x = airUtilData.map { it.time ?: 0 }, + y = airUtilData.map { it.device_metrics?.air_util_tx ?: 0f }, + ) + } + } } /* Series for Right Axis (Voltage) */ - lineSeries { - val voltageData = telemetries.filter { it.device_metrics?.voltage != null } - series( - x = voltageData.map { it.time ?: 0 }, - y = voltageData.map { it.device_metrics?.voltage ?: 0f }, - ) + if (voltageData.isNotEmpty()) { + lineSeries { + series( + x = voltageData.map { it.time ?: 0 }, + y = voltageData.map { it.device_metrics?.voltage ?: 0f }, + ) + } } } } - GenericMetricChart( - modelProducer = modelProducer, - modifier = Modifier.weight(1f).padding(horizontal = 8.dp).padding(bottom = 0.dp), - layers = - listOf( + val leftLayer = + if (leftLayerSeriesStyles.isNotEmpty()) { rememberLineCartesianLayer( - lineProvider = - LineCartesianLayer.LineProvider.series( - ChartStyling.createBoldLine( - lineColor = batteryColor, - pointSize = ChartStyling.MEDIUM_POINT_SIZE_DP, - ), - ChartStyling.createPointOnlyLine( - pointColor = chUtilColor, - pointSize = ChartStyling.LARGE_POINT_SIZE_DP, - ), - ChartStyling.createPointOnlyLine( - pointColor = airUtilColor, - pointSize = ChartStyling.LARGE_POINT_SIZE_DP, - ), - ), + lineProvider = LineCartesianLayer.LineProvider.series(leftLayerSeriesStyles), verticalAxisPosition = Axis.Position.Vertical.Start, - ), + ) + } else { + null + } + + val rightLayer = + if (voltageData.isNotEmpty()) { rememberLineCartesianLayer( lineProvider = LineCartesianLayer.LineProvider.series( @@ -320,30 +343,49 @@ private fun DeviceMetricsChart( ), ), verticalAxisPosition = Axis.Position.Vertical.End, + ) + } else { + null + } + + val layers = remember(leftLayer, rightLayer) { listOfNotNull(leftLayer, rightLayer) } + + if (layers.isNotEmpty()) { + GenericMetricChart( + modelProducer = modelProducer, + modifier = Modifier.weight(1f).padding(horizontal = 8.dp).padding(bottom = 0.dp), + layers = layers, + startAxis = + if (leftLayer != null) { + VerticalAxis.rememberStart( + label = ChartStyling.rememberAxisLabel(color = batteryColor), + valueFormatter = { _, value, _ -> "%.0f%%".format(value) }, + ) + } else { + null + }, + endAxis = + if (rightLayer != null) { + VerticalAxis.rememberEnd( + label = ChartStyling.rememberAxisLabel(color = voltageColor), + valueFormatter = { _, value, _ -> "%.1f V".format(value) }, + ) + } else { + null + }, + bottomAxis = + HorizontalAxis.rememberBottom( + label = ChartStyling.rememberAxisLabel(), + valueFormatter = CommonCharts.dynamicTimeFormatter, + itemPlacer = ChartStyling.rememberItemPlacer(spacing = 20), + labelRotationDegrees = 45f, ), - ), - startAxis = - VerticalAxis.rememberStart( - label = ChartStyling.rememberAxisLabel(color = batteryColor), - valueFormatter = { _, value, _ -> "%.0f%%".format(value) }, - ), - endAxis = - VerticalAxis.rememberEnd( - label = ChartStyling.rememberAxisLabel(color = voltageColor), - valueFormatter = { _, value, _ -> "%.1f V".format(value) }, - ), - bottomAxis = - HorizontalAxis.rememberBottom( - label = ChartStyling.rememberAxisLabel(), - valueFormatter = CommonCharts.dynamicTimeFormatter, - itemPlacer = ChartStyling.rememberItemPlacer(spacing = 20), - labelRotationDegrees = 45f, - ), - marker = marker, - selectedX = selectedX, - onPointSelected = onPointSelected, - vicoScrollState = vicoScrollState, - ) + marker = marker, + selectedX = selectedX, + onPointSelected = onPointSelected, + vicoScrollState = vicoScrollState, + ) + } Legend(legendData = legendData, modifier = Modifier.padding(top = 0.dp)) } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt index 4b397c7cd..60a034bfe 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentCharts.kt @@ -129,16 +129,41 @@ fun EnvironmentMetricsChart( } val colorToLabel = allLegendData.associate { it.color to stringResource(it.nameRes) } - LaunchedEffect(telemetries, graphData) { + val pressureData = + remember(telemetries) { + telemetries.filter { + val v = Environment.BAROMETRIC_PRESSURE.getValue(it) + it.time != 0 && v != null && !v.isNaN() + } + } + + val otherMetrics = + remember(telemetries, shouldPlot) { + Environment.entries.filter { metric -> + metric != Environment.BAROMETRIC_PRESSURE && + shouldPlot[metric.ordinal] && + telemetries.any { + val v = metric.getValue(it) + it.time != 0 && v != null && !v.isNaN() + } + } + } + + val otherMetricsData = + remember(telemetries, otherMetrics) { + otherMetrics.associateWith { metric -> + telemetries.filter { + val v = metric.getValue(it) + it.time != 0 && v != null && !v.isNaN() + } + } + } + + LaunchedEffect(pressureData, otherMetricsData) { modelProducer.runTransaction { /* Pressure on its own layer/axis */ - if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal]) { + if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal] && pressureData.isNotEmpty()) { lineSeries { - val pressureData = - telemetries.filter { - val v = Environment.BAROMETRIC_PRESSURE.getValue(it) - it.time != 0 && v != null && !v.isNaN() - } series( x = pressureData.map { it.time }, y = pressureData.map { Environment.BAROMETRIC_PRESSURE.getValue(it)!! }, @@ -146,14 +171,10 @@ fun EnvironmentMetricsChart( } } /* Everything else on the default axis */ - Environment.entries.forEach { metric -> - if (metric != Environment.BAROMETRIC_PRESSURE && shouldPlot[metric.ordinal]) { + otherMetrics.forEach { metric -> + val metricData = otherMetricsData[metric] ?: emptyList() + if (metricData.isNotEmpty()) { lineSeries { - val metricData = - telemetries.filter { - val v = metric.getValue(it) - it.time != 0 && v != null && !v.isNaN() - } series(x = metricData.map { it.time }, y = metricData.map { metric.getValue(it)!! }) } } @@ -171,7 +192,7 @@ fun EnvironmentMetricsChart( ) val layers = mutableListOf() - if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal]) { + if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal] && pressureData.isNotEmpty()) { layers.add( rememberLineCartesianLayer( lineProvider = @@ -185,31 +206,27 @@ fun EnvironmentMetricsChart( ), ) } - Environment.entries.forEach { metric -> - if (metric != Environment.BAROMETRIC_PRESSURE && shouldPlot[metric.ordinal]) { - layers.add( - rememberLineCartesianLayer( - lineProvider = - LineCartesianLayer.LineProvider.series( - ChartStyling.createGradientLine(metric.color, ChartStyling.MEDIUM_POINT_SIZE_DP), - ), - verticalAxisPosition = Axis.Position.Vertical.End, + otherMetrics.forEach { metric -> + layers.add( + rememberLineCartesianLayer( + lineProvider = + LineCartesianLayer.LineProvider.series( + ChartStyling.createGradientLine(metric.color, ChartStyling.MEDIUM_POINT_SIZE_DP), ), - ) - } + verticalAxisPosition = Axis.Position.Vertical.End, + ), + ) } if (layers.isNotEmpty()) { - val otherMetricsPlotted = - Environment.entries.filter { it != Environment.BAROMETRIC_PRESSURE && shouldPlot[it.ordinal] } - val endAxisColor = if (otherMetricsPlotted.size == 1) otherMetricsPlotted.first().color else onSurfaceColor + val endAxisColor = if (otherMetrics.size == 1) otherMetrics.first().color else onSurfaceColor GenericMetricChart( modelProducer = modelProducer, modifier = Modifier.weight(1f).padding(horizontal = 8.dp).padding(bottom = 0.dp), layers = layers, startAxis = - if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal]) { + if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal] && pressureData.isNotEmpty()) { VerticalAxis.rememberStart( label = ChartStyling.rememberAxisLabel(color = Environment.BAROMETRIC_PRESSURE.color), valueFormatter = { _, value, _ -> "%.0f hPa".format(value) }, diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt index 0eb38abbe..36bedd80f 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt @@ -197,15 +197,17 @@ fun PaxMetricsScreen(metricsViewModel: MetricsViewModel = hiltViewModel(), onNav // Prepare data for graph val graphData = - paxMetrics - .map { - val t = (it.first.received_date / CommonCharts.MS_PER_SEC).toInt() - Triple(t, it.second.ble ?: 0, it.second.wifi ?: 0) - } - .sortedBy { it.first } - val totalSeries = graphData.map { it.first to (it.second + it.third) } - val bleSeries = graphData.map { it.first to it.second } - val wifiSeries = graphData.map { it.first to it.third } + remember(paxMetrics) { + paxMetrics + .map { + val t = (it.first.received_date / CommonCharts.MS_PER_SEC).toInt() + Triple(t, it.second.ble ?: 0, it.second.wifi ?: 0) + } + .sortedBy { it.first } + } + val totalSeries = remember(graphData) { graphData.map { it.first to (it.second + it.third) } } + val bleSeries = remember(graphData) { graphData.map { it.first to it.second } } + val wifiSeries = remember(graphData) { graphData.map { it.first to it.third } } BaseMetricScreen( onNavigateUp = onNavigateUp, diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt index 1c779a02f..9f71aecc1 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt @@ -212,67 +212,100 @@ private fun PowerMetricsChart( }, ) - LaunchedEffect(telemetries, selectedChannel) { + val currentData = + remember(telemetries, selectedChannel) { + telemetries.filter { !retrieveCurrent(selectedChannel, it).isNaN() } + } + val voltageData = + remember(telemetries, selectedChannel) { + telemetries.filter { !retrieveVoltage(selectedChannel, it).isNaN() } + } + + LaunchedEffect(currentData, voltageData) { modelProducer.runTransaction { - lineSeries { - val currentData = telemetries.filter { !retrieveCurrent(selectedChannel, it).isNaN() } - series( - x = currentData.map { it.time ?: 0 }, - y = currentData.map { retrieveCurrent(selectedChannel, it) }, - ) + if (currentData.isNotEmpty()) { + lineSeries { + series( + x = currentData.map { it.time ?: 0 }, + y = currentData.map { retrieveCurrent(selectedChannel, it) }, + ) + } } - lineSeries { - val voltageData = telemetries.filter { !retrieveVoltage(selectedChannel, it).isNaN() } - series( - x = voltageData.map { it.time ?: 0 }, - y = voltageData.map { retrieveVoltage(selectedChannel, it) }, - ) + if (voltageData.isNotEmpty()) { + lineSeries { + series( + x = voltageData.map { it.time ?: 0 }, + y = voltageData.map { retrieveVoltage(selectedChannel, it) }, + ) + } } } } - GenericMetricChart( - modelProducer = modelProducer, - modifier = Modifier.weight(1f).padding(horizontal = 8.dp).padding(bottom = 0.dp), - layers = - listOf( + val currentLayer = + if (currentData.isNotEmpty()) { rememberLineCartesianLayer( lineProvider = LineCartesianLayer.LineProvider.series( ChartStyling.createBoldLine(currentColor, ChartStyling.MEDIUM_POINT_SIZE_DP), ), verticalAxisPosition = Axis.Position.Vertical.Start, - ), + ) + } else { + null + } + + val voltageLayer = + if (voltageData.isNotEmpty()) { rememberLineCartesianLayer( lineProvider = LineCartesianLayer.LineProvider.series( ChartStyling.createGradientLine(voltageColor, ChartStyling.MEDIUM_POINT_SIZE_DP), ), verticalAxisPosition = Axis.Position.Vertical.End, + ) + } else { + null + } + + val layers = remember(currentLayer, voltageLayer) { listOfNotNull(currentLayer, voltageLayer) } + + if (layers.isNotEmpty()) { + GenericMetricChart( + modelProducer = modelProducer, + modifier = Modifier.weight(1f).padding(horizontal = 8.dp).padding(bottom = 0.dp), + layers = layers, + startAxis = + if (currentData.isNotEmpty()) { + VerticalAxis.rememberStart( + label = ChartStyling.rememberAxisLabel(color = currentColor), + valueFormatter = { _, value, _ -> "%.0f mA".format(value) }, + ) + } else { + null + }, + endAxis = + if (voltageData.isNotEmpty()) { + VerticalAxis.rememberEnd( + label = ChartStyling.rememberAxisLabel(color = voltageColor), + valueFormatter = { _, value, _ -> "%.1f V".format(value) }, + ) + } else { + null + }, + bottomAxis = + HorizontalAxis.rememberBottom( + label = ChartStyling.rememberAxisLabel(), + valueFormatter = CommonCharts.dynamicTimeFormatter, + itemPlacer = ChartStyling.rememberItemPlacer(spacing = 50), + labelRotationDegrees = 45f, ), - ), - startAxis = - VerticalAxis.rememberStart( - label = ChartStyling.rememberAxisLabel(color = currentColor), - valueFormatter = { _, value, _ -> "%.0f mA".format(value) }, - ), - endAxis = - VerticalAxis.rememberEnd( - label = ChartStyling.rememberAxisLabel(color = voltageColor), - valueFormatter = { _, value, _ -> "%.1f V".format(value) }, - ), - bottomAxis = - HorizontalAxis.rememberBottom( - label = ChartStyling.rememberAxisLabel(), - valueFormatter = CommonCharts.dynamicTimeFormatter, - itemPlacer = ChartStyling.rememberItemPlacer(spacing = 50), - labelRotationDegrees = 45f, - ), - marker = marker, - selectedX = selectedX, - onPointSelected = onPointSelected, - vicoScrollState = vicoScrollState, - ) + marker = marker, + selectedX = selectedX, + onPointSelected = onPointSelected, + vicoScrollState = vicoScrollState, + ) + } Legend(legendData = LEGEND_DATA, modifier = Modifier.padding(top = 0.dp)) } diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt index 40d3e7691..f96df5ba1 100644 --- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt +++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt @@ -149,7 +149,7 @@ fun SignalMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigat ) } -@Suppress("LongMethod") +@Suppress("LongMethod", "CyclomaticComplexMethod") @Composable private fun SignalMetricsChart( modifier: Modifier = Modifier, @@ -162,24 +162,24 @@ private fun SignalMetricsChart( if (meshPackets.isEmpty()) return@Column val modelProducer = remember { CartesianChartModelProducer() } + val rssiColor = SignalMetric.RSSI.color + val snrColor = SignalMetric.SNR.color - LaunchedEffect(meshPackets) { + val rssiData = remember(meshPackets) { meshPackets.filter { (it.rx_rssi ?: 0) != 0 } } + val snrData = remember(meshPackets) { meshPackets.filter { !((it.rx_snr ?: Float.NaN).isNaN()) } } + + LaunchedEffect(rssiData, snrData) { modelProducer.runTransaction { - /* Use separate lineSeries calls to associate them with different vertical axes */ - lineSeries { - val rssiData = meshPackets.filter { (it.rx_rssi ?: 0) != 0 } - series(x = rssiData.map { it.rx_time ?: 0 }, y = rssiData.map { it.rx_rssi ?: 0 }) + if (rssiData.isNotEmpty()) { + /* Use separate lineSeries calls to associate them with different vertical axes */ + lineSeries { series(x = rssiData.map { it.rx_time ?: 0 }, y = rssiData.map { it.rx_rssi ?: 0 }) } } - lineSeries { - val snrData = meshPackets.filter { !((it.rx_snr ?: Float.NaN).isNaN()) } - series(x = snrData.map { it.rx_time ?: 0 }, y = snrData.map { it.rx_snr ?: 0f }) + if (snrData.isNotEmpty()) { + lineSeries { series(x = snrData.map { it.rx_time ?: 0 }, y = snrData.map { it.rx_snr ?: 0f }) } } } } - val rssiColor = SignalMetric.RSSI.color - val snrColor = SignalMetric.SNR.color - val marker = ChartStyling.rememberMarker( valueFormatter = @@ -192,48 +192,70 @@ private fun SignalMetricsChart( }, ) - GenericMetricChart( - modelProducer = modelProducer, - modifier = Modifier.weight(1f).padding(horizontal = 8.dp).padding(bottom = 0.dp), - layers = - listOf( + val rssiLayer = + if (rssiData.isNotEmpty()) { rememberLineCartesianLayer( lineProvider = LineCartesianLayer.LineProvider.series( ChartStyling.createPointOnlyLine(rssiColor, ChartStyling.LARGE_POINT_SIZE_DP), ), verticalAxisPosition = Axis.Position.Vertical.Start, - ), + ) + } else { + null + } + + val snrLayer = + if (snrData.isNotEmpty()) { rememberLineCartesianLayer( lineProvider = LineCartesianLayer.LineProvider.series( ChartStyling.createPointOnlyLine(snrColor, ChartStyling.LARGE_POINT_SIZE_DP), ), verticalAxisPosition = Axis.Position.Vertical.End, + ) + } else { + null + } + + val layers = remember(rssiLayer, snrLayer) { listOfNotNull(rssiLayer, snrLayer) } + + if (layers.isNotEmpty()) { + GenericMetricChart( + modelProducer = modelProducer, + modifier = Modifier.weight(1f).padding(horizontal = 8.dp).padding(bottom = 0.dp), + layers = layers, + startAxis = + if (rssiData.isNotEmpty()) { + VerticalAxis.rememberStart( + label = ChartStyling.rememberAxisLabel(color = rssiColor), + valueFormatter = { _, value, _ -> "%.0f dBm".format(value) }, + ) + } else { + null + }, + endAxis = + if (snrData.isNotEmpty()) { + VerticalAxis.rememberEnd( + label = ChartStyling.rememberAxisLabel(color = snrColor), + valueFormatter = { _, value, _ -> "%.1f dB".format(value) }, + ) + } else { + null + }, + bottomAxis = + HorizontalAxis.rememberBottom( + label = ChartStyling.rememberAxisLabel(), + valueFormatter = CommonCharts.dynamicTimeFormatter, + itemPlacer = ChartStyling.rememberItemPlacer(spacing = 50), + labelRotationDegrees = 45f, ), - ), - startAxis = - VerticalAxis.rememberStart( - label = ChartStyling.rememberAxisLabel(color = rssiColor), - valueFormatter = { _, value, _ -> "%.0f dBm".format(value) }, - ), - endAxis = - VerticalAxis.rememberEnd( - label = ChartStyling.rememberAxisLabel(color = snrColor), - valueFormatter = { _, value, _ -> "%.1f dB".format(value) }, - ), - bottomAxis = - HorizontalAxis.rememberBottom( - label = ChartStyling.rememberAxisLabel(), - valueFormatter = CommonCharts.dynamicTimeFormatter, - itemPlacer = ChartStyling.rememberItemPlacer(spacing = 50), - labelRotationDegrees = 45f, - ), - marker = marker, - selectedX = selectedX, - onPointSelected = onPointSelected, - vicoScrollState = vicoScrollState, - ) + marker = marker, + selectedX = selectedX, + onPointSelected = onPointSelected, + vicoScrollState = vicoScrollState, + ) + } Legend(legendData = LEGEND_DATA, modifier = Modifier.padding(top = 0.dp)) }