Skip to content

Commit 28bba30

Browse files
committed
Add pinch to zoom, enable live graph updates with finger motion
1 parent 7e75517 commit 28bba30

File tree

7 files changed

+51
-65
lines changed

7 files changed

+51
-65
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ android {
3434
getDefaultProguardFile("proguard-android-optimize.txt"),
3535
"proguard-rules.pro"
3636
)
37+
signingConfig = signingConfigs.getByName("debug")
3738
}
3839
}
3940
compileOptions {

app/src/main/cpp/native-lib.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ JNIEXPORT jfloatArray JNICALL
2626
Java_org_nsh07_simplygraph_NativeBridge_calculateGraphPoints(
2727
JNIEnv *env,
2828
jobject,
29-
jint xWidth,
29+
jdouble xWidth,
3030
jdouble yWidth,
3131
jdouble xOffset,
3232
jdouble yOffset,

app/src/main/java/org/nsh07/simplygraph/NativeBridge.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class NativeBridge {
66
}
77

88
external fun calculateGraphPoints(
9-
xWidth: Int,
9+
xWidth: Double,
1010
yWidth: Double,
1111
xOffset: Double,
1212
yOffset: Double,

app/src/main/java/org/nsh07/simplygraph/ui/AppScreen.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ fun AppScreen(modifier: Modifier = Modifier) {
7474
}
7575

7676
val colorScheme = colorScheme
77-
val transformableState = rememberTransformableState { _, offsetChange, _ ->
78-
viewModel.updateOffset(offsetChange)
77+
val transformableState = rememberTransformableState { scaleChange, offsetChange, _ ->
78+
viewModel.updateScaleOffset(scaleChange, offsetChange)
7979
}
8080
val scaffoldState = rememberBottomSheetScaffoldState()
8181
val topSpacing by animateDpAsState(
@@ -288,7 +288,7 @@ fun AppScreen(modifier: Modifier = Modifier) {
288288
)
289289
}
290290
FloatingActionButton(
291-
onClick = viewModel::resetOffset,
291+
onClick = viewModel::resetOrigin,
292292
modifier = Modifier
293293
.align(Alignment.BottomEnd)
294294
.padding(bottom = insets.calculateBottomPadding())

app/src/main/java/org/nsh07/simplygraph/ui/viewModel/UiState.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ data class GraphState(
99
val invalidations: Int = 0,
1010
val canvasSize: Size = Size(0f, 0f),
1111
val points: List<Offset> = emptyList(),
12-
val xWidth: Int = 10,
12+
val xWidth: Float = 10f,
1313
val xOffset: Float = 0f,
1414
val yOffset: Float = 0f,
1515
val connectPoints: Boolean = true

app/src/main/java/org/nsh07/simplygraph/ui/viewModel/UiViewModel.kt

Lines changed: 43 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import androidx.compose.ui.geometry.Offset
44
import androidx.compose.ui.geometry.Size
55
import androidx.lifecycle.ViewModel
66
import androidx.lifecycle.viewModelScope
7-
import kotlinx.coroutines.CancellationException
87
import kotlinx.coroutines.Dispatchers
98
import kotlinx.coroutines.Job
109
import kotlinx.coroutines.delay
@@ -23,8 +22,7 @@ class UiViewModel : ViewModel() {
2322
val functionsState: StateFlow<FunctionsState> = _functionsState.asStateFlow()
2423

2524
private var nativeBridge = NativeBridge()
26-
private var calculationJob: Job? = null
27-
private var updateJob: Job? = null
25+
private var reloadJob: Job? = null
2826

2927
fun updateFunction(function: String) {
3028
_functionsState.update { currentState ->
@@ -41,32 +39,31 @@ class UiViewModel : ViewModel() {
4139
canvasSize = size
4240
)
4341
}
44-
updateJob?.cancel()
45-
updateJob = viewModelScope.launch {
46-
delay(50)
47-
updateGraph()
48-
}
42+
updateGraph()
4943
}
5044

51-
fun updateOffset(offsetChange: Offset) {
45+
fun updateScaleOffset(scaleChange: Float, offsetChange: Offset) {
5246
_graphState.update { currentState ->
5347
currentState.copy(
5448
xOffset = currentState.xOffset + offsetChange.x,
55-
yOffset = currentState.yOffset + offsetChange.y
49+
yOffset = currentState.yOffset + offsetChange.y,
50+
xWidth = currentState.xWidth / scaleChange
5651
)
5752
}
58-
updateJob?.cancel()
59-
updateJob = viewModelScope.launch {
60-
delay(50)
61-
updateGraph()
53+
if (reloadJob?.isActive != true) {
54+
reloadJob = viewModelScope.launch {
55+
updateGraph()
56+
delay(16) // Limit to roughly 60fps max
57+
}
6258
}
6359
}
6460

65-
fun resetOffset() {
61+
fun resetOrigin() {
6662
_graphState.update { currentState ->
6763
currentState.copy(
6864
xOffset = 0f,
69-
yOffset = 0f
65+
yOffset = 0f,
66+
xWidth = 10f
7067
)
7168
}
7269
updateGraph()
@@ -106,48 +103,36 @@ class UiViewModel : ViewModel() {
106103

107104
fun updateGraph() {
108105
if (functionsState.value.function.isNotEmpty()) {
109-
calculationJob?.cancel()
110-
calculationJob = viewModelScope.launch(Dispatchers.IO) {
111-
try {
112-
val xWidth =
113-
graphState.value.xWidth // Number of integral x-coordinates visible on screen
114-
val yWidth =
115-
xWidth * (graphState.value.canvasSize.height / graphState.value.canvasSize.width)
116-
val function = functionsState.value.function
117-
val canvasSize = graphState.value.canvasSize
118-
119-
// We use the native C++ function for calculating graph points to plot faster
120-
121-
val newPoints = nativeBridge
122-
.calculateGraphPoints(
123-
xWidth = xWidth,
124-
yWidth = yWidth.toDouble(),
125-
xOffset = graphState.value.xOffset.toDouble(),
126-
yOffset = graphState.value.yOffset.toDouble(),
127-
canvasWidth = canvasSize.width.toDouble(),
128-
canvasHeight = canvasSize.height.toDouble(),
129-
tStart = functionsState.value.tStart,
130-
tEnd = functionsState.value.tEnd,
131-
thetaStart = functionsState.value.thetaStart,
132-
thetaEnd = functionsState.value.thetaEnd,
133-
function = function
134-
)
135-
.toOffsetList()
136-
137-
_graphState.update { currentState ->
138-
currentState.copy(
139-
invalidations = currentState.invalidations + 1,
140-
points = newPoints
141-
)
142-
}
143-
} catch (e: Exception) {
144-
if (e !is CancellationException)
145-
_graphState.update { currentState ->
146-
currentState.copy(
147-
invalidations = currentState.invalidations + 1,
148-
points = emptyList()
149-
)
150-
}
106+
viewModelScope.launch(Dispatchers.IO) {
107+
val xWidth =
108+
graphState.value.xWidth // Number of integral x-coordinates visible on screen
109+
val yWidth =
110+
xWidth * (graphState.value.canvasSize.height / graphState.value.canvasSize.width)
111+
val function = functionsState.value.function
112+
val canvasSize = graphState.value.canvasSize
113+
114+
// We use the native C++ function for calculating graph points to plot faster
115+
val newPoints = nativeBridge
116+
.calculateGraphPoints(
117+
xWidth = xWidth.toDouble(),
118+
yWidth = yWidth.toDouble(),
119+
xOffset = graphState.value.xOffset.toDouble(),
120+
yOffset = graphState.value.yOffset.toDouble(),
121+
canvasWidth = canvasSize.width.toDouble(),
122+
canvasHeight = canvasSize.height.toDouble(),
123+
tStart = functionsState.value.tStart,
124+
tEnd = functionsState.value.tEnd,
125+
thetaStart = functionsState.value.thetaStart,
126+
thetaEnd = functionsState.value.thetaEnd,
127+
function = function
128+
)
129+
.toOffsetList()
130+
131+
_graphState.update { currentState ->
132+
currentState.copy(
133+
invalidations = currentState.invalidations + 1,
134+
points = newPoints
135+
)
151136
}
152137
}
153138
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Fri May 23 20:36:09 IST 2025
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)