From 89ad4dc350c4afe77857f3493efe181bba8f07cb Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Sun, 22 Jun 2025 12:40:01 +0000 Subject: [PATCH] Fix/2100 graph labels (#2188) Co-authored-by: Dane Evans --- .../mesh/ui/metrics/CommonCharts.kt | 13 +- .../mesh/ui/metrics/DeviceMetrics.kt | 152 ++++++++++++++++-- .../mesh/ui/metrics/EnvironmentMetrics.kt | 61 +++++-- .../mesh/ui/metrics/PowerMetrics.kt | 39 +++-- .../mesh/ui/metrics/SignalMetrics.kt | 43 +++-- 5 files changed, 257 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/ui/metrics/CommonCharts.kt b/app/src/main/java/com/geeksville/mesh/ui/metrics/CommonCharts.kt index 1813e5869..82a3b8e0b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/metrics/CommonCharts.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/metrics/CommonCharts.kt @@ -57,14 +57,16 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.geeksville.mesh.R -import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT import com.geeksville.mesh.ui.metrics.CommonCharts.MAX_PERCENT_VALUE import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC +import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_MINUTE_FORMAT import java.text.DateFormat object CommonCharts { val DATE_TIME_FORMAT: DateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM) + val TIME_MINUTE_FORMAT: DateFormat = DateFormat.getTimeInstance(DateFormat.SHORT) + val DATE_TIME_MINUTE_FORMAT: DateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT) const val MS_PER_SEC = 1000L const val MAX_PERCENT_VALUE = 100f } @@ -190,7 +192,7 @@ fun TimeAxisOverlay( Canvas(modifier = modifier) { val height = size.height - val width = size.width - 28.dp.toPx() + val width = size.width /* Cut out the time remaining in order to place the lines on the dot. */ val timeRemaining = oldest % timeInterval @@ -247,18 +249,19 @@ fun TimeAxisOverlay( @Composable fun TimeLabels( oldest: Int, - newest: Int + newest: Int, ) { + Row { Text( - text = DATE_TIME_FORMAT.format(oldest * MS_PER_SEC), + text = DATE_TIME_MINUTE_FORMAT.format(oldest * MS_PER_SEC), modifier = Modifier.wrapContentWidth(), style = TextStyle(fontWeight = FontWeight.Bold), fontSize = 12.sp, ) Spacer(modifier = Modifier.weight(1f)) Text( - text = DATE_TIME_FORMAT.format(newest * MS_PER_SEC), + text = DATE_TIME_MINUTE_FORMAT.format(newest * MS_PER_SEC), modifier = Modifier.wrapContentWidth(), style = TextStyle(fontWeight = FontWeight.Bold), fontSize = 12.sp diff --git a/app/src/main/java/com/geeksville/mesh/ui/metrics/DeviceMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/metrics/DeviceMetrics.kt index 8aee0fcc4..a7e17e911 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/metrics/DeviceMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/metrics/DeviceMetrics.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -58,12 +59,14 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.geeksville.mesh.R +import com.geeksville.mesh.TelemetryProtos import com.geeksville.mesh.TelemetryProtos.Telemetry import com.geeksville.mesh.model.MetricsViewModel import com.geeksville.mesh.model.TimeFrame import com.geeksville.mesh.ui.common.components.BatteryInfo import com.geeksville.mesh.ui.common.components.OptionLabel import com.geeksville.mesh.ui.common.components.SlidingSelector +import com.geeksville.mesh.ui.common.theme.AppTheme import com.geeksville.mesh.ui.common.theme.Orange import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT import com.geeksville.mesh.ui.metrics.CommonCharts.MAX_PERCENT_VALUE @@ -71,6 +74,7 @@ import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC import com.geeksville.mesh.util.GraphUtil import com.geeksville.mesh.util.GraphUtil.createPath import com.geeksville.mesh.util.GraphUtil.plotPoint +import androidx.compose.ui.tooling.preview.PreviewLightDark private enum class Device(val color: Color) { BATTERY(Color.Green), @@ -78,6 +82,10 @@ private enum class Device(val color: Color) { AIR_UTIL(Color.Cyan) } +private const val CHART_WEIGHT = 1f +private const val Y_AXIS_WEIGHT = 0.1f +private const val CHART_WIDTH_RATIO = CHART_WEIGHT / (CHART_WEIGHT + Y_AXIS_WEIGHT) + private val LEGEND_DATA = listOf( LegendData(nameRes = R.string.battery, color = Device.BATTERY.color, isLine = true), LegendData(nameRes = R.string.channel_utilization, color = Device.CH_UTIL.color), @@ -139,6 +147,7 @@ private fun DeviceMetricsChart( selectedTime: TimeFrame, promptInfoDialog: () -> Unit ) { + val graphColor = MaterialTheme.colorScheme.onSurface ChartHeader(amount = telemetries.size) if (telemetries.isEmpty()) return @@ -151,21 +160,33 @@ private fun DeviceMetricsChart( } val timeDiff = newest.time - oldest.time - TimeLabels( - oldest = oldest.time, - newest = newest.time - ) - - Spacer(modifier = Modifier.height(16.dp)) - - val graphColor = MaterialTheme.colorScheme.onSurface - val scrollState = rememberScrollState() val screenWidth = LocalWindowInfo.current.containerSize.width val dp by remember(key1 = selectedTime) { mutableStateOf(selectedTime.dp(screenWidth, time = timeDiff.toLong())) } + // Calculate visible time range based on scroll position and chart width + val visibleTimeRange = run { + val totalWidthPx = with(LocalDensity.current) { dp.toPx() } + val scrollPx = scrollState.value.toFloat() + // Calculate visible width based on actual weight distribution + val visibleWidthPx = screenWidth * CHART_WIDTH_RATIO + val leftRatio = (scrollPx / totalWidthPx).coerceIn(0f, 1f) + val rightRatio = ((scrollPx + visibleWidthPx) / totalWidthPx).coerceIn(0f, 1f) + // With reverseScrolling = true, scrolling right shows older data (left side of chart) + val visibleOldest = oldest.time + (timeDiff * (1f - rightRatio)).toInt() + val visibleNewest = oldest.time + (timeDiff * (1f - leftRatio)).toInt() + visibleOldest to visibleNewest + } + + TimeLabels( + oldest = visibleTimeRange.first, + newest = visibleTimeRange.second, + ) + + Spacer(modifier = Modifier.height(16.dp)) + Row { Box( contentAlignment = Alignment.TopStart, @@ -252,7 +273,7 @@ private fun DeviceMetricsChart( } } YAxisLabels( - modifier = modifier.weight(weight = .1f), + modifier = modifier.weight(weight = Y_AXIS_WEIGHT), graphColor, minValue = 0f, maxValue = 100f @@ -265,6 +286,34 @@ private fun DeviceMetricsChart( Spacer(modifier = Modifier.height(16.dp)) } +@Suppress("detekt:MagicNumber") // fake data +@PreviewLightDark +@Composable +private fun DeviceMetricsChartPreview() { + val now = (System.currentTimeMillis() / 1000).toInt() + val telemetries = List(20) { i -> + Telemetry.newBuilder() + .setTime(now - (19 - i) * 60 * 60) // 1-hour intervals, oldest first + .setDeviceMetrics( + TelemetryProtos.DeviceMetrics.newBuilder() + .setBatteryLevel(80 - i) + .setVoltage(3.7f - i * 0.02f) + .setChannelUtilization(10f + i * 2) + .setAirUtilTx(5f + i) + .setUptimeSeconds(3600 + i * 300) + ) + .build() + } + AppTheme { + DeviceMetricsChart( + modifier = Modifier.height(400.dp), + telemetries = telemetries, + selectedTime = TimeFrame.TWENTY_FOUR_HOURS, + promptInfoDialog = {} + ) + } +} + @Composable private fun DeviceMetricsCard(telemetry: Telemetry) { val deviceMetrics = telemetry.deviceMetrics @@ -321,3 +370,86 @@ private fun DeviceMetricsCard(telemetry: Telemetry) { } } } + +@Suppress("detekt:MagicNumber") // fake data +@PreviewLightDark +@Composable +private fun DeviceMetricsCardPreview() { + val now = (System.currentTimeMillis() / 1000).toInt() + val telemetry = Telemetry.newBuilder() + .setTime(now) + .setDeviceMetrics( + TelemetryProtos.DeviceMetrics.newBuilder() + .setBatteryLevel(75) + .setVoltage(3.65f) + .setChannelUtilization(22.5f) + .setAirUtilTx(12.0f) + .setUptimeSeconds(7200) + ) + .build() + AppTheme { + DeviceMetricsCard(telemetry = telemetry) + } +} + +@Suppress("detekt:MagicNumber") // fake data +@PreviewLightDark +@Composable +private fun DeviceMetricsScreenPreview() { + val now = (System.currentTimeMillis() / 1000).toInt() + val telemetries = List(24) { i -> + Telemetry.newBuilder() + .setTime(now - (23 - i) * 60 * 60) // 1-hour intervals, oldest first + .setDeviceMetrics( + TelemetryProtos.DeviceMetrics.newBuilder() + .setBatteryLevel(85 - i * 2) // Battery decreases over time + .setVoltage(3.8f - i * 0.01f) // Voltage decreases slightly + .setChannelUtilization(15f + i * 1.5f) // Channel utilization increases + .setAirUtilTx(8f + i * 0.8f) // Air utilization increases + .setUptimeSeconds(3600 + i * 3600) // Uptime increases by 1 hour each + ) + .build() + } + + AppTheme { + Surface { + Column { + var displayInfoDialog by remember { mutableStateOf(false) } + + if (displayInfoDialog) { + LegendInfoDialog( + pairedRes = listOf( + Pair(R.string.channel_utilization, R.string.ch_util_definition), + Pair(R.string.air_utilization, R.string.air_util_definition) + ), + onDismiss = { displayInfoDialog = false } + ) + } + + DeviceMetricsChart( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(fraction = 0.33f), + telemetries.reversed(), + TimeFrame.TWENTY_FOUR_HOURS, + promptInfoDialog = { displayInfoDialog = true } + ) + + SlidingSelector( + TimeFrame.entries.toList(), + TimeFrame.TWENTY_FOUR_HOURS, + onOptionSelected = { /* Preview only */ } + ) { + OptionLabel(stringResource(it.strRes)) + } + + /* Device Metric Cards */ + LazyColumn( + modifier = Modifier.fillMaxSize() + ) { + items(telemetries) { telemetry -> DeviceMetricsCard(telemetry) } + } + } + } + } +} diff --git a/app/src/main/java/com/geeksville/mesh/ui/metrics/EnvironmentMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/metrics/EnvironmentMetrics.kt index 6585dacf6..4df84ea30 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/metrics/EnvironmentMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/metrics/EnvironmentMetrics.kt @@ -47,8 +47,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Path -import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -72,6 +74,20 @@ import com.geeksville.mesh.util.GraphUtil.createPath import com.geeksville.mesh.util.GraphUtil.drawPathWithGradient import com.geeksville.mesh.util.UnitConversions.celsiusToFahrenheit +@Suppress("MagicNumber") +private enum class Environment(val color: Color) { + TEMPERATURE(Color.Red), + RELATIVE_HUMIDITY(Color.Blue), + BAROMETRIC_PRESSURE(Color.Green), + GAS_RESISTANCE(Color.Yellow), + IAQ(Color.Magenta) +} + +private const val CHART_WEIGHT = 1f +private const val Y_AXIS_WEIGHT = 0.1f +// EnvironmentMetrics can have 1 or 2 Y-axis labels depending on whether barometric pressure is plotted +// We'll calculate this dynamically in the chart function + private val LEGEND_DATA_1 = listOf( LegendData( nameRes = R.string.temperature, @@ -165,7 +181,6 @@ fun EnvironmentMetricsScreen( } } -/* TODO need to take the time to understand this. */ @SuppressLint("ConfigurationScreenWidthHeight") @Suppress("LongMethod") @Composable @@ -182,9 +197,35 @@ private fun EnvironmentMetricsChart( } val (oldest, newest) = graphData.times + val timeDiff = newest - oldest + + val scrollState = rememberScrollState() + val screenWidth = LocalWindowInfo.current.containerSize.width + val dp by remember(key1 = selectedTime) { + mutableStateOf(selectedTime.dp(screenWidth, time = timeDiff.toLong())) + } + + val shouldPlot = graphData.shouldPlot + + // Calculate visible time range based on scroll position and chart width + val visibleTimeRange = run { + val totalWidthPx = with(LocalDensity.current) { dp.toPx() } + val scrollPx = scrollState.value.toFloat() + // Calculate chart width ratio dynamically based on whether barometric pressure is plotted + val yAxisCount = if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal]) 2 else 1 + val chartWidthRatio = CHART_WEIGHT / (CHART_WEIGHT + (Y_AXIS_WEIGHT * yAxisCount)) + val visibleWidthPx = screenWidth * chartWidthRatio + val leftRatio = (scrollPx / totalWidthPx).coerceIn(0f, 1f) + val rightRatio = ((scrollPx + visibleWidthPx) / totalWidthPx).coerceIn(0f, 1f) + // With reverseScrolling = true, scrolling right shows older data (left side of chart) + val visibleOldest = oldest + (timeDiff * (1f - rightRatio)).toInt() + val visibleNewest = oldest + (timeDiff * (1f - leftRatio)).toInt() + visibleOldest to visibleNewest + } + TimeLabels( - oldest = oldest, - newest = newest + oldest = visibleTimeRange.first, + newest = visibleTimeRange.second ) Spacer(modifier = Modifier.height(16.dp)) @@ -196,18 +237,10 @@ private fun EnvironmentMetricsChart( var min = rightMin var diff = rightMax - rightMin - val scrollState = rememberScrollState() - val screenWidth = LocalConfiguration.current.screenWidthDp - val timeDiff = newest - oldest - val dp by remember(key1 = selectedTime) { - mutableStateOf(selectedTime.dp(screenWidth, time = timeDiff.toLong())) - } - val shouldPlot = graphData.shouldPlot - Row { if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal]) { YAxisLabels( - modifier = modifier.weight(weight = .1f), + modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Environment.BAROMETRIC_PRESSURE.color, minValue = pressureMin, maxValue = pressureMax @@ -277,7 +310,7 @@ private fun EnvironmentMetricsChart( } } YAxisLabels( - modifier = modifier.weight(weight = .1f), + modifier = modifier.weight(weight = Y_AXIS_WEIGHT), graphColor, minValue = rightMin, maxValue = rightMax diff --git a/app/src/main/java/com/geeksville/mesh/ui/metrics/PowerMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/metrics/PowerMetrics.kt index 1b3ac360d..e67310879 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/metrics/PowerMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/metrics/PowerMetrics.kt @@ -51,6 +51,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -87,6 +88,10 @@ private enum class PowerChannel(@StringRes val strRes: Int) { THREE(R.string.channel_3) } +private const val CHART_WEIGHT = 1f +private const val Y_AXIS_WEIGHT = 0.1f +private const val CHART_WIDTH_RATIO = CHART_WEIGHT / (CHART_WEIGHT + Y_AXIS_WEIGHT + Y_AXIS_WEIGHT) + private val LEGEND_DATA = listOf( LegendData(nameRes = R.string.current, color = Power.CURRENT.color, isLine = true), LegendData(nameRes = R.string.voltage, color = Power.VOLTAGE.color, isLine = true), @@ -157,9 +162,29 @@ private fun PowerMetricsChart( } val timeDiff = newest.time - oldest.time + val scrollState = rememberScrollState() + val screenWidth = LocalWindowInfo.current.containerSize.width + val dp by remember(key1 = selectedTime) { + mutableStateOf(selectedTime.dp(screenWidth, time = (newest.time - oldest.time).toLong())) + } + + // Calculate visible time range based on scroll position and chart width + val visibleTimeRange = run { + val totalWidthPx = with(LocalDensity.current) { dp.toPx() } + val scrollPx = scrollState.value.toFloat() + // Calculate visible width based on actual weight distribution + val visibleWidthPx = screenWidth * CHART_WIDTH_RATIO + val leftRatio = (scrollPx / totalWidthPx).coerceIn(0f, 1f) + val rightRatio = ((scrollPx + visibleWidthPx) / totalWidthPx).coerceIn(0f, 1f) + // With reverseScrolling = true, scrolling right shows older data (left side of chart) + val visibleOldest = oldest.time + (timeDiff * (1f - rightRatio)).toInt() + val visibleNewest = oldest.time + (timeDiff * (1f - leftRatio)).toInt() + visibleOldest to visibleNewest + } + TimeLabels( - oldest = oldest.time, - newest = newest.time + oldest = visibleTimeRange.first, + newest = visibleTimeRange.second ) Spacer(modifier = Modifier.height(16.dp)) @@ -168,15 +193,9 @@ private fun PowerMetricsChart( val currentDiff = Power.CURRENT.difference() val voltageDiff = Power.VOLTAGE.difference() - val scrollState = rememberScrollState() - val screenWidth = LocalWindowInfo.current.containerSize.width - val dp by remember(key1 = selectedTime) { - mutableStateOf(selectedTime.dp(screenWidth, time = (newest.time - oldest.time).toLong())) - } - Row { YAxisLabels( - modifier = modifier.weight(weight = .1f), + modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Power.CURRENT.color, minValue = Power.CURRENT.min, maxValue = Power.CURRENT.max, @@ -263,7 +282,7 @@ private fun PowerMetricsChart( } } YAxisLabels( - modifier = modifier.weight(weight = .1f), + modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Power.VOLTAGE.color, minValue = Power.VOLTAGE.min, maxValue = Power.VOLTAGE.max, diff --git a/app/src/main/java/com/geeksville/mesh/ui/metrics/SignalMetrics.kt b/app/src/main/java/com/geeksville/mesh/ui/metrics/SignalMetrics.kt index 8b9d88b82..e371ca85e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/metrics/SignalMetrics.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/metrics/SignalMetrics.kt @@ -48,7 +48,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -76,6 +77,11 @@ private enum class Metric(val color: Color, val min: Float, val max: Float) { */ fun difference() = max - min } + +private const val CHART_WEIGHT = 1f +private const val Y_AXIS_WEIGHT = 0.1f +private const val CHART_WIDTH_RATIO = CHART_WEIGHT / (CHART_WEIGHT + Y_AXIS_WEIGHT + Y_AXIS_WEIGHT) + private val LEGEND_DATA = listOf( LegendData(nameRes = R.string.rssi, color = Metric.RSSI.color), LegendData(nameRes = R.string.snr, color = Metric.SNR.color) @@ -148,9 +154,29 @@ private fun SignalMetricsChart( } val timeDiff = newest.rxTime - oldest.rxTime + val scrollState = rememberScrollState() + val screenWidth = LocalWindowInfo.current.containerSize.width + val dp by remember(key1 = selectedTime) { + mutableStateOf(selectedTime.dp(screenWidth, time = (newest.rxTime - oldest.rxTime).toLong())) + } + + // Calculate visible time range based on scroll position and chart width + val visibleTimeRange = run { + val totalWidthPx = with(LocalDensity.current) { dp.toPx() } + val scrollPx = scrollState.value.toFloat() + // Calculate visible width based on actual weight distribution + val visibleWidthPx = screenWidth * CHART_WIDTH_RATIO + val leftRatio = (scrollPx / totalWidthPx).coerceIn(0f, 1f) + val rightRatio = ((scrollPx + visibleWidthPx) / totalWidthPx).coerceIn(0f, 1f) + // With reverseScrolling = true, scrolling right shows older data (left side of chart) + val visibleOldest = oldest.rxTime + (timeDiff * (1f - rightRatio)).toInt() + val visibleNewest = oldest.rxTime + (timeDiff * (1f - leftRatio)).toInt() + visibleOldest to visibleNewest + } + TimeLabels( - oldest = oldest.rxTime, - newest = newest.rxTime + oldest = visibleTimeRange.first, + newest = visibleTimeRange.second ) Spacer(modifier = Modifier.height(16.dp)) @@ -159,16 +185,9 @@ private fun SignalMetricsChart( val snrDiff = Metric.SNR.difference() val rssiDiff = Metric.RSSI.difference() - val scrollState = rememberScrollState() - val configuration = LocalConfiguration.current - val screenWidth = configuration.screenWidthDp - val dp by remember(key1 = selectedTime) { - mutableStateOf(selectedTime.dp(screenWidth, time = (newest.rxTime - oldest.rxTime).toLong())) - } - Row { YAxisLabels( - modifier = modifier.weight(weight = .1f), + modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Metric.RSSI.color, minValue = Metric.RSSI.min, maxValue = Metric.RSSI.max, @@ -221,7 +240,7 @@ private fun SignalMetricsChart( } } YAxisLabels( - modifier = modifier.weight(weight = .1f), + modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Metric.SNR.color, minValue = Metric.SNR.min, maxValue = Metric.SNR.max,