From e3abf0a20538c6c3f80166b9d18675fabf68830d Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Mon, 6 May 2024 11:48:49 +0530 Subject: [PATCH] feat: improve theming and text rendering --- .../calcyou/ui/screens/graphing/CanvasView.kt | 8 +- .../ui/screens/graphing/GraphingUtils.kt | 78 ++++++++++++++----- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/net/youapps/calcyou/ui/screens/graphing/CanvasView.kt b/app/src/main/java/net/youapps/calcyou/ui/screens/graphing/CanvasView.kt index 18434d9..02d7152 100644 --- a/app/src/main/java/net/youapps/calcyou/ui/screens/graphing/CanvasView.kt +++ b/app/src/main/java/net/youapps/calcyou/ui/screens/graphing/CanvasView.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.scale import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.positionChange @@ -23,11 +22,10 @@ import net.youapps.calcyou.viewmodels.GraphViewModel @Composable fun CanvasView(vm: GraphViewModel) { - Box(Modifier.background(Color.White)) { + Box(Modifier.background(MaterialTheme.colorScheme.background)) { val textMeasurer = rememberTextMeasurer() val drawModifier = Modifier .fillMaxSize() - .background(MaterialTheme.colorScheme.background) .pointerInput(Unit) { awaitEachGesture { val downEvent = awaitFirstDown() @@ -86,10 +84,12 @@ fun CanvasView(vm: GraphViewModel) { } while (event.changes.any { it.pressed }) } } + val gridLinesColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.2f) + val gridAxesColor = MaterialTheme.colorScheme.onBackground Canvas(modifier = drawModifier) { val scale = 1f scale(scale) { - renderCanvas(vm.window, vm, scale, textMeasurer) + renderCanvas(vm.window, vm, scale, textMeasurer, gridLinesColor, gridAxesColor) } } } diff --git a/app/src/main/java/net/youapps/calcyou/ui/screens/graphing/GraphingUtils.kt b/app/src/main/java/net/youapps/calcyou/ui/screens/graphing/GraphingUtils.kt index efff926..24ede45 100644 --- a/app/src/main/java/net/youapps/calcyou/ui/screens/graphing/GraphingUtils.kt +++ b/app/src/main/java/net/youapps/calcyou/ui/screens/graphing/GraphingUtils.kt @@ -5,15 +5,19 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.text.TextMeasurer +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.drawText +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import net.youapps.calcyou.data.graphing.Function import net.youapps.calcyou.data.graphing.Window import net.youapps.calcyou.data.graphing.unitToPxCoordinates import net.youapps.calcyou.viewmodels.GraphViewModel +import kotlin.math.abs import kotlin.math.ceil import kotlin.math.floor -fun DrawScope.drawGridLines(window: Window, lineWidth: Float) { +fun DrawScope.drawGridLines(window: Window, lineWidth: Float, gridLinesColor: Color) { val xRange = IntRange( ceil(window.xMin / window.xScale).toInt(), floor(window.xMax / window.xScale).toInt() @@ -23,7 +27,7 @@ fun DrawScope.drawGridLines(window: Window, lineWidth: Float) { if (i == 0) continue val xDraw = Offset(i * window.xScale, 0f).unitToPxCoordinates(window, size.width, size.height).x - drawLine(Color.LightGray, Offset(xDraw, 0f), Offset(xDraw, size.height), lineWidth) + drawLine(gridLinesColor, Offset(xDraw, 0f), Offset(xDraw, size.height), lineWidth) } val yRange = IntRange( ceil(window.yMin / window.yScale).toInt(), @@ -34,25 +38,49 @@ fun DrawScope.drawGridLines(window: Window, lineWidth: Float) { if (i == 0) continue val yDraw = Offset(0f, i * window.yScale).unitToPxCoordinates(window, size.width, size.height).y - drawLine(Color.LightGray, Offset(0f, yDraw), Offset(size.width, yDraw), lineWidth) + drawLine(gridLinesColor, Offset(0f, yDraw), Offset(size.width, yDraw), lineWidth) + } +} + +internal fun Float.toDisplayString(): String { + return when { + (this % 1f == 0f) && (this in 0.0001f..10000f) -> { + this.toInt().toString() + } + + abs(this) <= 0.0001 -> { + "%.3e".format(this) + } + + abs(this) < 10000 -> { + this.toString() + } + + else -> { + "%.2e".format(this) + } } } fun DrawScope.drawAxes( - window: Window, lineWidth: Float, canvasScale: Float, textMeasurer: TextMeasurer + window: Window, + lineWidth: Float, + canvasScale: Float, + textMeasurer: TextMeasurer, + axesColor: Color ) { // y-axis val windowCenterInCanvas = Offset(0f, 0f).unitToPxCoordinates(window, size.width, size.height) drawLine( - Color.Black, + axesColor, Offset(windowCenterInCanvas.x, 0f), Offset(windowCenterInCanvas.x, size.height), lineWidth ) // x-axis drawLine( - Color.Black, + axesColor, Offset(0f, windowCenterInCanvas.y), Offset(size.width, windowCenterInCanvas.y), lineWidth @@ -66,25 +94,32 @@ fun DrawScope.drawAxes( for (i in xTickRange) { if (i == 0) continue - val xDisplayValue = (i * window.xScale).toString() + val xDisplayValue = (i * window.xScale).toDisplayString() val xDraw = Offset(i * window.xScale, 0f).unitToPxCoordinates(window, size.width, size.height).x val yDraw = Offset(0f, 0f).unitToPxCoordinates(window, size.width, size.height).y // Ticks and labels val halfTickLength = 10f / canvasScale - val textOffset = 15f / canvasScale drawLine( - Color.Black, + axesColor, Offset(xDraw, yDraw + halfTickLength), Offset(xDraw, yDraw - halfTickLength), lineWidth ) + val textWidth = 200 / canvasScale + val textHeight = 40 / canvasScale + val textPadding = 20 / canvasScale drawText( textMeasurer, xDisplayValue, - topLeft = Offset(xDraw, yDraw + textOffset), - size = Size(50 / canvasScale, 40 / canvasScale) + topLeft = Offset(xDraw - textWidth / 2, yDraw + textPadding), + size = Size(textWidth, textHeight), + style = TextStyle( + color = axesColor, + textAlign = TextAlign.Center, + fontWeight = FontWeight.Medium + ) ) } @@ -95,24 +130,27 @@ fun DrawScope.drawAxes( for (i in yTickRange) { if (i == 0) continue - val yDisplayValue = (i * window.yScale).toString() + val yDisplayValue = (i * window.yScale).toDisplayString() val xDraw = Offset(0f, 0f).unitToPxCoordinates(window, size.width, size.height).x val yDraw = Offset(0f, i * window.yScale).unitToPxCoordinates(window, size.width, size.height).y val halfTickLength = 10f / canvasScale - val textOffset = 15f / canvasScale drawLine( - Color.Black, + axesColor, Offset(xDraw - halfTickLength, yDraw), Offset(xDraw + halfTickLength, yDraw), lineWidth ) + val textWidth = 200 / canvasScale + val textHeight = 40 / canvasScale + val textPadding = 20 / canvasScale drawText( textMeasurer, yDisplayValue, - topLeft = Offset(xDraw - textOffset, yDraw), - size = Size(50 / canvasScale, 40 / canvasScale) + topLeft = Offset(xDraw + textPadding, yDraw - textHeight / 2), + size = Size(textWidth, textHeight), + style = TextStyle(color = axesColor, fontWeight = FontWeight.Medium) ) } } @@ -141,12 +179,14 @@ fun DrawScope.renderCanvas( window: Window, vm: GraphViewModel, canvasScale: Float, - textMeasurer: TextMeasurer + textMeasurer: TextMeasurer, + gridLinesColor: Color, + axesColor: Color ) { val lineWidth = 5f / canvasScale window.findAutoScale() - drawGridLines(window, lineWidth) - drawAxes(window, lineWidth, canvasScale, textMeasurer) + drawGridLines(window, lineWidth, gridLinesColor) + drawAxes(window, lineWidth, canvasScale, textMeasurer, axesColor) vm.functions.forEach { drawGraph(window, it, lineWidth) } } \ No newline at end of file