Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ data class Function(
val expression: String,
val color: Color,
val name: String,
val compiled: CompiledExpression
val compiled: CompiledExpression,
val isVisible: Boolean = true
) {
fun execute(variableX: Float, mode: TrigonometricMode, constants: List<Constant>): Float? {
val variables = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ package net.youapps.calcyou.ui.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AssistChip
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
Expand All @@ -32,6 +38,7 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import net.youapps.calcyou.R
import net.youapps.calcyou.viewmodels.GraphViewModel
Expand Down Expand Up @@ -80,13 +87,14 @@ private fun DialogContent(
.clip(RoundedCornerShape(16.dp))
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp))
) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(
text = stringResource(R.string.add_new_function),
style = MaterialTheme.typography.titleLarge
)
var text by remember { mutableStateOf(initialExpression) }
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = text,
onValueChange = {
text = it
Expand Down Expand Up @@ -118,11 +126,60 @@ private fun DialogContent(
fontWeight = FontWeight.Medium,
fontSize = MaterialTheme.typography.bodyLarge.fontSize,
fontStyle = FontStyle.Italic
)
),
supportingText = {
Text(
text = "Supports nested functions like sin(cos(x))",
Comment thread
SuhasDissa marked this conversation as resolved.
Outdated
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
)
AnimatedVisibility(isError) {
Text(text = errorMessage, color = MaterialTheme.colorScheme.error)
}

// Function template chips
Text(
text = "Quick Templates:",
Comment thread
SuhasDissa marked this conversation as resolved.
Outdated
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
val templates = listOf(
"sin(x)" to "sin(x)",
"x^2" to "x^2",
"sin(cos(x))" to "sin(cos(x))",
"sqrt(x^2+1)" to "sqrt(x^2+1)",
"e^(-x^2)" to "exp(-x^2)",
"ln(abs(x))" to "ln(abs(x))",
"tan(x/2)" to "tan(x/2)"
)

templates.forEach { (label, template) ->
Comment thread
SuhasDissa marked this conversation as resolved.
Outdated
AssistChip(
onClick = {
text = template
checkExpression(template)
},
label = {
Text(
text = label,
style = TextStyle(
fontFamily = FontFamily.Serif,
fontStyle = FontStyle.Italic,
fontSize = 12.sp
)
)
}
)
}
}
Row(Modifier.align(Alignment.End)) {
TextButton(onClick = onCancel) {
Text(text = stringResource(id = android.R.string.cancel))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,33 @@ import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.calculateCentroid
import androidx.compose.foundation.gestures.calculatePan
import androidx.compose.foundation.gestures.calculateZoom
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import net.youapps.calcyou.data.graphing.pxToUnitCoordinates
import net.youapps.calcyou.viewmodels.GraphViewModel
Expand Down Expand Up @@ -93,6 +110,59 @@ fun CanvasView(vm: GraphViewModel) {
renderCanvas(vm.window, vm, scale, textMeasurer, gridLinesColor, gridAxesColor)
}
}

// Legend overlay showing visible functions
if (vm.functions.isNotEmpty()) {
GraphLegend(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(16.dp),
functions = vm.functions.filter { it.isVisible }
)
}
}
}

@Composable
fun GraphLegend(
modifier: Modifier = Modifier,
functions: List<net.youapps.calcyou.data.graphing.Function>
) {
Surface(
modifier = modifier,
shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.85f),
shadowElevation = 4.dp
) {
Column(
modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
functions.forEach { function ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
Box(
modifier = Modifier
.size(12.dp)
.clip(CircleShape)
.background(function.color)
)
Text(
text = "${function.name}(x) = ${function.expression}",
style = TextStyle(
fontFamily = FontFamily.Serif,
fontWeight = FontWeight.Medium,
fontSize = 12.sp,
fontStyle = FontStyle.Italic,
color = MaterialTheme.colorScheme.onSurface
),
maxLines = 1
)
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Clear
import androidx.compose.material.icons.rounded.Visibility
import androidx.compose.material.icons.rounded.VisibilityOff
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.Button
import androidx.compose.material3.Divider
Expand Down Expand Up @@ -81,6 +83,9 @@ fun GraphingScreen(graphViewModel: GraphViewModel = viewModel()) {
onClickRemoveFunction = {
graphViewModel.removeFunction(it)
},
onToggleFunctionVisibility = {
graphViewModel.toggleFunctionVisibility(it)
},
onClickConstant = {
graphViewModel.selectedConstantIndex = it
showAddConstantDialog = true
Expand Down Expand Up @@ -158,6 +163,7 @@ fun FunctionConstantsList(
constants: List<Constant>,
onClickFunction: (Int) -> Unit,
onClickRemoveFunction: (Int) -> Unit,
onToggleFunctionVisibility: (Int) -> Unit,
onClickConstant: (Int) -> Unit,
onClickRemoveConstant: (Int) -> Unit
) {
Expand All @@ -169,9 +175,13 @@ fun FunctionConstantsList(
lhsName = "${function.name}(x)",
text = function.expression,
color = function.color,
isVisible = function.isVisible,
onClick = { onClickFunction(index) },
onClickRemove = {
onClickRemoveFunction(index)
},
onToggleVisibility = {
onToggleFunctionVisibility(index)
}
)
Divider(Modifier.fillMaxWidth())
Expand All @@ -195,7 +205,9 @@ fun FunctionRow(
text: String,
color: Color?,
onClick: () -> Unit,
onClickRemove: () -> Unit
onClickRemove: () -> Unit,
isVisible: Boolean = true,
onToggleVisibility: (() -> Unit)? = null
) {
Row(
modifier = Modifier
Expand Down Expand Up @@ -223,6 +235,15 @@ fun FunctionRow(
)
)
Spacer(modifier = Modifier.weight(1f))
if (onToggleVisibility != null) {
IconButton(onClick = { onToggleVisibility() }) {
Icon(
imageVector = if (isVisible) Icons.Rounded.Visibility else Icons.Rounded.VisibilityOff,
contentDescription = if (isVisible) "Hide function" else "Show function",
tint = if (isVisible) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outline
)
}
}
IconButton(onClick = { onClickRemove() }) {
Icon(
imageVector = Icons.Rounded.Clear,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,26 @@ fun DrawScope.drawGridLines(window: Window, lineWidth: Float, gridLinesColor: Co
floor(window.xMax / window.xScale).toInt()
)

// Draw vertical grid lines with subtle styling
for (i in xRange) {
if (i == 0) continue
val xDraw =
Offset(i * window.xScale, 0f).unitToPxCoordinates(window, size.width, size.height).x
drawLine(gridLinesColor, Offset(xDraw, 0f), Offset(xDraw, size.height), lineWidth)
val alpha = if (i % 5 == 0) 0.3f else 0.15f // Emphasize every 5th line
drawLine(gridLinesColor.copy(alpha = alpha), Offset(xDraw, 0f), Offset(xDraw, size.height), lineWidth)
}
val yRange = IntRange(
ceil(window.yMin / window.yScale).toInt(),
floor(window.yMax / window.yScale).toInt()
)

// Draw horizontal grid lines with subtle styling
for (i in yRange) {
if (i == 0) continue
val yDraw =
Offset(0f, i * window.yScale).unitToPxCoordinates(window, size.width, size.height).y
drawLine(gridLinesColor, Offset(0f, yDraw), Offset(size.width, yDraw), lineWidth)
val alpha = if (i % 5 == 0) 0.3f else 0.15f // Emphasize every 5th line
drawLine(gridLinesColor.copy(alpha = alpha), Offset(0f, yDraw), Offset(size.width, yDraw), lineWidth)
}
}

Expand Down Expand Up @@ -71,20 +75,20 @@ fun DrawScope.drawAxes(
axesColor: Color
) {

// y-axis
// y-axis - draw with slightly thicker line for prominence
val windowCenterInCanvas = Offset(0f, 0f).unitToPxCoordinates(window, size.width, size.height)
drawLine(
axesColor,
axesColor.copy(alpha = 0.8f),
Offset(windowCenterInCanvas.x, 0f),
Offset(windowCenterInCanvas.x, size.height),
lineWidth
lineWidth * 1.5f
)
// x-axis
// x-axis - draw with slightly thicker line for prominence
drawLine(
axesColor,
axesColor.copy(alpha = 0.8f),
Offset(0f, windowCenterInCanvas.y),
Offset(size.width, windowCenterInCanvas.y),
lineWidth
lineWidth * 1.5f
)

// Ticks on x-axis
Expand Down Expand Up @@ -290,10 +294,14 @@ fun DrawScope.renderCanvas(
drawGridLines(window, lineWidth, gridLinesColor)
drawAxes(window, lineWidth, canvasScale, textMeasurer, axesColor)

// Draw functions with slightly thicker lines for better visibility
val graphLineWidth = lineWidth * 1.8f
vm.functions.forEach {
// this might fail if we attempt to use a constant that no longer exists
runCatching {
drawGraph(window, it, vm.constants, lineWidth, vm.mode)
if (it.isVisible) {
runCatching {
drawGraph(window, it, vm.constants, graphLineWidth, vm.mode)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ class GraphViewModel(private val application: Application) : AndroidViewModel(ap
}
}

fun toggleFunctionVisibility(index: Int) {
val function = functions[index]
functions[index] = function.copy(isVisible = !function.isVisible)
}

private fun getFuncName(index: Int): String {
return Defaults.defaultFuncNameChars[
index % Defaults.defaultFuncNameChars.size
Expand Down