From 272ef3482f690e6f70ac1b3cabc8adeb34bcf415 Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:03 +0300 Subject: [PATCH 01/26] Add new fragment --- .../fragment/calculator/CalculatorFragment.kt | 5 ++ .../fragment/graphic/GraphicFragment.kt | 29 ++++++++++ .../main/res/layout/fragment_calculator.xml | 13 +++++ app/src/main/res/layout/fragment_graphic.xml | 53 +++++++++++++++++++ app/src/main/res/navigation/navigation.xml | 7 +++ app/src/main/res/values/strings.xml | 1 + 6 files changed, 108 insertions(+) create mode 100644 app/src/main/java/com/fintamath/fragment/graphic/GraphicFragment.kt create mode 100644 app/src/main/res/layout/fragment_graphic.xml diff --git a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt index 3f8ddc0e..d4571304 100644 --- a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt @@ -178,6 +178,7 @@ class CalculatorFragment : Fragment() { viewBinding.historyButton.setOnClickListener { showHistoryFragment() } viewBinding.settingsButton.setOnClickListener { showSettingsFragment() } viewBinding.aboutButton.setOnClickListener { showAboutFragment() } + viewBinding.graphicButton.setOnClickListener { showGraphicFragment() } } private fun updateSettings() { @@ -314,6 +315,10 @@ class CalculatorFragment : Fragment() { showFragment(R.id.action_calculatorFragment_to_settingsFragment) } + private fun showGraphicFragment() { + showFragment(R.id.action_calculatorFragment_to_graphicFragment) + } + private fun showFragment(navigationId: Int) { runSaveToHistoryTask() viewBinding.inTextView.clearFocusInWeb() diff --git a/app/src/main/java/com/fintamath/fragment/graphic/GraphicFragment.kt b/app/src/main/java/com/fintamath/fragment/graphic/GraphicFragment.kt new file mode 100644 index 00000000..fbd14c60 --- /dev/null +++ b/app/src/main/java/com/fintamath/fragment/graphic/GraphicFragment.kt @@ -0,0 +1,29 @@ +package com.fintamath.fragment.graphic + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.navigation.findNavController +import com.fintamath.databinding.FragmentGraphicBinding + +class GraphicFragment : Fragment() { + + private lateinit var viewBinding: FragmentGraphicBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + viewBinding = FragmentGraphicBinding.inflate(inflater, container, false) + + viewBinding.aboutBackButton.setOnClickListener { executeBack() } + + return viewBinding.root + } + + private fun executeBack() { + viewBinding.root.findNavController().navigateUp() + } +} diff --git a/app/src/main/res/layout/fragment_calculator.xml b/app/src/main/res/layout/fragment_calculator.xml index 7c184b79..b6f03e3f 100644 --- a/app/src/main/res/layout/fragment_calculator.xml +++ b/app/src/main/res/layout/fragment_calculator.xml @@ -62,6 +62,19 @@ android:src="@drawable/ic_history" android:contentDescription="@string/history"/> + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml index 048d71ee..094ab4b4 100644 --- a/app/src/main/res/navigation/navigation.xml +++ b/app/src/main/res/navigation/navigation.xml @@ -26,6 +26,9 @@ + @@ -72,5 +75,9 @@ android:name="com.fintamath.fragment.settings.SettingsFragment" android:label="fragment_settings" tools:layout="@layout/fragment_settings" /> + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7a774583..4dcbc6a7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,6 +5,7 @@ Back Camera History + Graphic Edit Settings Bookmark From 1106f0135a6b8e3387b18f417a34a61e9e547897 Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:03 +0300 Subject: [PATCH 02/26] Add GraphView widget --- .../com/fintamath/widget/graph/DataPoint.kt | 6 ++ .../com/fintamath/widget/graph/GraphView.kt | 78 +++++++++++++++++++ app/src/main/res/layout/fragment_graphic.xml | 10 ++- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/fintamath/widget/graph/DataPoint.kt create mode 100644 app/src/main/java/com/fintamath/widget/graph/GraphView.kt diff --git a/app/src/main/java/com/fintamath/widget/graph/DataPoint.kt b/app/src/main/java/com/fintamath/widget/graph/DataPoint.kt new file mode 100644 index 00000000..58ef2e8e --- /dev/null +++ b/app/src/main/java/com/fintamath/widget/graph/DataPoint.kt @@ -0,0 +1,6 @@ +package com.fintamath.widget.graph + +data class DataPoint( + val xVal: Int, + val yVal: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt new file mode 100644 index 00000000..fd2d9ac8 --- /dev/null +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -0,0 +1,78 @@ +package com.fintamath.widget.graph + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View + +class GraphView( + context: Context, + attributeSet: AttributeSet +) : View(context, attributeSet) { + + private val dataSet = mutableListOf() + private var xMin = 0 + private var xMax = 0 + private var yMin = 0 + private var yMax = 0 + + private val dataPointPaint = Paint().apply { + color = Color.BLUE + strokeWidth = 7f + style = Paint.Style.STROKE + } + + private val dataPointFillPaint = Paint().apply { + color = Color.WHITE + } + + private val dataPointLinePaint = Paint().apply { + color = Color.BLUE + strokeWidth = 7f + isAntiAlias = true + } + + private val axisLinePaint = Paint().apply { + color = Color.RED + strokeWidth = 4f + } + + fun setData(newDataSet: List) { + xMin = newDataSet.minBy { it.xVal }.xVal ?: 0 + xMax = newDataSet.maxBy { it.xVal }.xVal ?: 0 + yMin = newDataSet.minBy { it.yVal }.yVal ?: 0 + yMax = newDataSet.maxBy { it.yVal }.yVal ?: 0 + dataSet.clear() + dataSet.addAll(newDataSet) + invalidate() + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + dataSet.forEachIndexed { index, currentDataPoint -> + val realX = currentDataPoint.xVal.toRealX() + val realY = currentDataPoint.yVal.toRealY() + + if (index < dataSet.size - 1) { + val nextDataPoint = dataSet[index + 1] + val startX = currentDataPoint.xVal.toRealX() + val startY = currentDataPoint.yVal.toRealY() + val endX = nextDataPoint.xVal.toRealX() + val endY = nextDataPoint.yVal.toRealY() + canvas.drawLine(startX, startY, endX, endY, dataPointLinePaint) + } + + canvas.drawCircle(realX, realY, 7f, dataPointFillPaint) + canvas.drawCircle(realX, realY, 7f, dataPointPaint) + } + + canvas.drawLine(width.toFloat() / 2, 0f, width.toFloat() / 2, height.toFloat(), axisLinePaint) + canvas.drawLine(0f, height.toFloat() / 2, width.toFloat(), height.toFloat() / 2, axisLinePaint) + } + private fun Int.toRealX() = toFloat() / xMax * width + private fun Int.toRealY() = toFloat() / yMax * height + +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_graphic.xml b/app/src/main/res/layout/fragment_graphic.xml index 891eaee8..3cde72b3 100644 --- a/app/src/main/res/layout/fragment_graphic.xml +++ b/app/src/main/res/layout/fragment_graphic.xml @@ -47,7 +47,13 @@ + android:layout_height="match_parent" + android:orientation="vertical"> + + + + \ No newline at end of file From 600d06c5bd0d69b6d8de84b3ef5a5972b19b9ad1 Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:03 +0300 Subject: [PATCH 03/26] Add scroll to GraphView --- .../com/fintamath/widget/graph/GraphView.kt | 133 +++++++++++------- 1 file changed, 82 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt index fd2d9ac8..3a58d46d 100644 --- a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -1,78 +1,109 @@ package com.fintamath.widget.graph +import android.annotation.SuppressLint import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint +import android.view.MotionEvent import android.util.AttributeSet import android.view.View +import kotlin.math.abs +import kotlin.math.min class GraphView( - context: Context, - attributeSet: AttributeSet -) : View(context, attributeSet) { - - private val dataSet = mutableListOf() - private var xMin = 0 - private var xMax = 0 - private var yMin = 0 - private var yMax = 0 - - private val dataPointPaint = Paint().apply { - color = Color.BLUE - strokeWidth = 7f - style = Paint.Style.STROKE + ctx: Context, + attrs: AttributeSet +) : View(ctx, attrs) { + + private val axisPaint = Paint().apply { + color = Color.LTGRAY + strokeWidth = 3f } - private val dataPointFillPaint = Paint().apply { - color = Color.WHITE + private val gridPaint = Paint().apply { + color = Color.DKGRAY + strokeWidth = 2f } - private val dataPointLinePaint = Paint().apply { - color = Color.BLUE - strokeWidth = 7f - isAntiAlias = true + private var offsetX = 0.0f + private var offsetY = 0.0f + + private var cellSize: Float = 0.0f + private val minCellCount: Int = 10 + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + val width = width.toFloat() + val height = height.toFloat() + + cellSize = min(height, width) / minCellCount + + drawGrid(canvas) + + if (abs(offsetX) < width / 2) { + drawVerticalAxis(canvas) + } + + if (abs(offsetY) < height / 2) { + drawHorizontalAxis(canvas) + } + } - private val axisLinePaint = Paint().apply { - color = Color.RED - strokeWidth = 4f + private fun drawHorizontalAxis(canvas: Canvas) { + val offsetHeight = height.toFloat() / 2 + offsetY + val width = width.toFloat() + canvas.drawLine(0f, offsetHeight, width, offsetHeight, axisPaint) } - fun setData(newDataSet: List) { - xMin = newDataSet.minBy { it.xVal }.xVal ?: 0 - xMax = newDataSet.maxBy { it.xVal }.xVal ?: 0 - yMin = newDataSet.minBy { it.yVal }.yVal ?: 0 - yMax = newDataSet.maxBy { it.yVal }.yVal ?: 0 - dataSet.clear() - dataSet.addAll(newDataSet) - invalidate() + private fun drawVerticalAxis(canvas: Canvas) { + val height = height.toFloat() + val offsetWidth = width.toFloat() / 2 + offsetX + canvas.drawLine(offsetWidth, 0f, offsetWidth, height, axisPaint) } - override fun onDraw(canvas: Canvas) { - super.onDraw(canvas) + private var lastX = 0f + private var lastY = 0f - dataSet.forEachIndexed { index, currentDataPoint -> - val realX = currentDataPoint.xVal.toRealX() - val realY = currentDataPoint.yVal.toRealY() - - if (index < dataSet.size - 1) { - val nextDataPoint = dataSet[index + 1] - val startX = currentDataPoint.xVal.toRealX() - val startY = currentDataPoint.yVal.toRealY() - val endX = nextDataPoint.xVal.toRealX() - val endY = nextDataPoint.yVal.toRealY() - canvas.drawLine(startX, startY, endX, endY, dataPointLinePaint) - } + private fun drawGrid(canvas: Canvas) { + var currentXCoord = offsetX % cellSize + var currentYCoord = offsetY % cellSize - canvas.drawCircle(realX, realY, 7f, dataPointFillPaint) - canvas.drawCircle(realX, realY, 7f, dataPointPaint) + while (currentXCoord < width.toFloat()) { + canvas.drawLine(currentXCoord, 0.0f, currentXCoord, height.toFloat(), gridPaint) + currentXCoord += cellSize } - canvas.drawLine(width.toFloat() / 2, 0f, width.toFloat() / 2, height.toFloat(), axisLinePaint) - canvas.drawLine(0f, height.toFloat() / 2, width.toFloat(), height.toFloat() / 2, axisLinePaint) + while (currentYCoord < height.toFloat()) { + canvas.drawLine(0.0f, currentYCoord, width.toFloat(), currentYCoord, gridPaint) + currentYCoord += cellSize + } } - private fun Int.toRealX() = toFloat() / xMax * width - private fun Int.toRealY() = toFloat() / yMax * height + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + val x = event.x + val y = event.y + + when (event.action) { + MotionEvent.ACTION_DOWN -> { + lastX = x + lastY = y + } + MotionEvent.ACTION_MOVE -> { + val deltaX = x - lastX + val deltaY = y - lastY + + offsetX += deltaX + offsetY += deltaY + + lastX = x + lastY = y + } + } + invalidate() + return true + } } \ No newline at end of file From 5409c97c2931fdfbb9d8a05307b7e76ed91e24fa Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:03 +0300 Subject: [PATCH 04/26] Add functions for graph drawing --- .../com/fintamath/calculator/Calculator.kt | 6 +++ .../calculator/CalculatorProcessor.kt | 7 +++ .../fragment/calculator/CalculatorFragment.kt | 30 +++++++++++ lib/src/Fintamath.cpp | 50 +++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/app/src/main/java/com/fintamath/calculator/Calculator.kt b/app/src/main/java/com/fintamath/calculator/Calculator.kt index a698cccc..e60d4834 100644 --- a/app/src/main/java/com/fintamath/calculator/Calculator.kt +++ b/app/src/main/java/com/fintamath/calculator/Calculator.kt @@ -16,6 +16,12 @@ internal class Calculator( external fun setPrecision(int: Int) + external fun approximate(exprStr: String, varStr: String, valStr: String): String + + external fun getVariableCount(exprStr: String): Int + + external fun getLastVariable(exprStr: String): String + private fun onCalculated(str: String) { calculatedCallback.invoke(listOf(*str.split("\n").toTypedArray())) } diff --git a/app/src/main/java/com/fintamath/calculator/CalculatorProcessor.kt b/app/src/main/java/com/fintamath/calculator/CalculatorProcessor.kt index a69bb724..f1c15177 100644 --- a/app/src/main/java/com/fintamath/calculator/CalculatorProcessor.kt +++ b/app/src/main/java/com/fintamath/calculator/CalculatorProcessor.kt @@ -50,6 +50,13 @@ class CalculatorProcessor( fun setPrecision(precision: Int) = calculator.setPrecision(precision) + fun approximate(exprStr: String, varStr: String, valStr: String): String = + calculator.approximate(exprStr, varStr, valStr) + + fun getVariableCount(exprStr: String): Int = calculator.getVariableCount(exprStr) + + fun getLastVariable(exprStr: String): String = calculator.getLastVariable(exprStr) + private fun onCalculated(result: List) { callbacksThread { isCalculating.set(false) diff --git a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt index d4571304..a2f67623 100644 --- a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt @@ -2,6 +2,7 @@ package com.fintamath.fragment.calculator import android.graphics.drawable.ColorDrawable import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent import android.view.View @@ -17,11 +18,13 @@ import com.fintamath.storage.MathTextData import com.fintamath.storage.SettingsStorage import com.fintamath.widget.keyboard.Keyboard import com.fintamath.widget.keyboard.KeyboardView +import java.math.BigDecimal import java.util.Timer import java.util.TimerTask import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger import kotlin.concurrent.schedule +import kotlin.concurrent.thread class CalculatorFragment : Fragment() { @@ -246,6 +249,7 @@ class CalculatorFragment : Fragment() { viewBinding.outSolutionView.showFailedToSolve() } else { val cutSolutionTexts = cutSolutionTexts(texts) + val firstSolutionText = cutSolutionTexts.first() if (countTextsLength(cutSolutionTexts) > maxSolutionLength) { viewBinding.outSolutionView.showCharacterLimitExceeded() @@ -258,6 +262,32 @@ class CalculatorFragment : Fragment() { } } } + + thread { + drawGraph(firstSolutionText) + } + } + } + + private fun drawGraph(firstSolutionText: String) { + if (calculatorProcessor.getVariableCount(firstSolutionText) == 1) { + val varStr = calculatorProcessor.getLastVariable(firstSolutionText) + val minVal = BigDecimal(-10) + val maxVal = BigDecimal(10) + val delta = BigDecimal("0.1") + + var i = minVal + while (i <= maxVal) { + Log.d( + "fintamath", + calculatorProcessor.approximate( + firstSolutionText, + varStr, + i.toString() + ) + ) + i += delta + } } } diff --git a/lib/src/Fintamath.cpp b/lib/src/Fintamath.cpp index 1b2588da..32990b02 100644 --- a/lib/src/Fintamath.cpp +++ b/lib/src/Fintamath.cpp @@ -2,6 +2,7 @@ #include "fintamath/expressions/Expression.hpp" #include "fintamath/expressions/ExpressionFunctions.hpp" #include "fintamath/expressions/ExpressionParser.hpp" +#include "fintamath/numbers/Real.hpp" #include #include @@ -25,6 +26,10 @@ static unsigned currPrecision = 10; static pid_t calcPid = -1; static auto *solutionStrShared = (char *)mmap(nullptr, maxSolutionLength, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); +static std::string exprStrCached; +static Expression exprCached; +static std::vector exprVariablesCached; + std::string makeOutResult(const std::string &res) { return res + "\n"; } @@ -62,6 +67,16 @@ void stopCurrentCalculations() { } } +void updateCachedExpression(const std::string &exprStr) { + if (exprStrCached != exprStr) { + exprStrCached = exprStr; + exprCached = Expression(exprStrCached); + exprVariablesCached = exprCached.getVariables(); + + __android_log_print(ANDROID_LOG_DEBUG, loggerTag, "aaa"); + } +} + extern "C" JNIEXPORT void Java_com_fintamath_calculator_Calculator_calculate(JNIEnv *env, jobject instance, jstring inJStr) { stopCurrentCalculations(); @@ -129,3 +144,38 @@ extern "C" JNIEXPORT void Java_com_fintamath_calculator_Calculator_setPrecision( currPrecision = inPrecision; } } + +extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Calculator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { + std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); + std::string varStr = env->GetStringUTFChars(varJStr, nullptr); + std::string valStr = env->GetStringUTFChars(valJStr, nullptr); + + updateCachedExpression(exprStr); + + Expression approxExpr = exprCached; + approxExpr.setVariable(Variable(varStr), Real(valStr)); + approxExpr = approxExpr.approximate(); + + if (auto res = cast(approxExpr.toMinimalObject())) { + return env->NewStringUTF(res->toString(5).c_str()); + } + + return env->NewStringUTF(""); +} + +extern "C" JNIEXPORT jint Java_com_fintamath_calculator_Calculator_getVariableCount(JNIEnv *env, jobject instance, jstring exprJStr) { + std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); + updateCachedExpression(exprStr); + return jint(exprVariablesCached.size()); +} + +extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Calculator_getLastVariable(JNIEnv *env, jobject instance, jstring exprJStr) { + std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); + updateCachedExpression(exprStr); + + if (exprVariablesCached.empty()) { + return env->NewStringUTF(""); + } + + return env->NewStringUTF(exprVariablesCached.back().toString().c_str()); +} From ca70a0d41e3780e2e33e292a30052d3d4c5997c8 Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 05/26] Improve graph drawing performance --- .../fragment/calculator/CalculatorFragment.kt | 63 +++++++++++++------ lib/src/Fintamath.cpp | 2 - 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt index a2f67623..fd8df2bb 100644 --- a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt @@ -23,8 +23,8 @@ import java.util.Timer import java.util.TimerTask import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicLong import kotlin.concurrent.schedule -import kotlin.concurrent.thread class CalculatorFragment : Fragment() { @@ -44,6 +44,8 @@ class CalculatorFragment : Fragment() { private var wereSettingsUpdated = AtomicBoolean(false) + private var graphThreadId = AtomicLong(0) + private val maxSolutionLength = 2000 private val inTextViewPreloadString = "abc * 123 Pi E I Inf ComplexInf () sqrt() abs() floor() ceil() derivative(x,x)" @@ -263,32 +265,57 @@ class CalculatorFragment : Fragment() { } } - thread { - drawGraph(firstSolutionText) - } + drawGraph(firstSolutionText) } } private fun drawGraph(firstSolutionText: String) { - if (calculatorProcessor.getVariableCount(firstSolutionText) == 1) { + val graphThread = Thread { + if (calculatorProcessor.getVariableCount(firstSolutionText) != 1) { + return@Thread + } + val varStr = calculatorProcessor.getLastVariable(firstSolutionText) - val minVal = BigDecimal(-10) - val maxVal = BigDecimal(10) + val min = BigDecimal(-10) + val max = BigDecimal(10) + val mid = (min + max) / BigDecimal(2) + + drawGraphPoint(firstSolutionText, varStr, mid) + val delta = BigDecimal("0.1") + var bottom = mid - delta + var top = mid + delta - var i = minVal - while (i <= maxVal) { - Log.d( - "fintamath", - calculatorProcessor.approximate( - firstSolutionText, - varStr, - i.toString() - ) - ) - i += delta + while (bottom >= min) { + if (Thread.currentThread().id != graphThreadId.get()) { + break + } + + drawGraphPoint(firstSolutionText, varStr, bottom) + drawGraphPoint(firstSolutionText, varStr, top) + + bottom -= delta + top += delta } } + + graphThreadId.set(graphThread.id) + graphThread.start() + } + + private fun drawGraphPoint( + firstSolutionText: String, + varStr: String, + top: BigDecimal + ) { + Log.d( + "fintamath", + calculatorProcessor.approximate( + firstSolutionText, + varStr, + top.toString() + ) + ) } private fun startLoading() { diff --git a/lib/src/Fintamath.cpp b/lib/src/Fintamath.cpp index 32990b02..bda4fd7e 100644 --- a/lib/src/Fintamath.cpp +++ b/lib/src/Fintamath.cpp @@ -72,8 +72,6 @@ void updateCachedExpression(const std::string &exprStr) { exprStrCached = exprStr; exprCached = Expression(exprStrCached); exprVariablesCached = exprCached.getVariables(); - - __android_log_print(ANDROID_LOG_DEBUG, loggerTag, "aaa"); } } From 0d49783cff91196de1003d759149b486bba3ad7f Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 06/26] Use coroutine in drawGraph --- app/build.gradle | 1 + .../fragment/calculator/CalculatorFragment.kt | 20 +++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ddc3d047..bb532ee1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,4 +68,5 @@ dependencies { implementation 'com.intuit.ssp:ssp-android:1.1.0' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' } \ No newline at end of file diff --git a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt index fd8df2bb..502d76e5 100644 --- a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt @@ -8,6 +8,7 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import com.fintamath.R import com.fintamath.calculator.CalculatorProcessor @@ -18,12 +19,14 @@ import com.fintamath.storage.MathTextData import com.fintamath.storage.SettingsStorage import com.fintamath.widget.keyboard.Keyboard import com.fintamath.widget.keyboard.KeyboardView +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.yield import java.math.BigDecimal import java.util.Timer import java.util.TimerTask import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicLong import kotlin.concurrent.schedule @@ -44,7 +47,7 @@ class CalculatorFragment : Fragment() { private var wereSettingsUpdated = AtomicBoolean(false) - private var graphThreadId = AtomicLong(0) + private var drawGraphJob: Job? = null private val maxSolutionLength = 2000 @@ -270,9 +273,11 @@ class CalculatorFragment : Fragment() { } private fun drawGraph(firstSolutionText: String) { - val graphThread = Thread { + drawGraphJob?.cancel() + + drawGraphJob = viewLifecycleOwner.lifecycleScope.launch { if (calculatorProcessor.getVariableCount(firstSolutionText) != 1) { - return@Thread + return@launch } val varStr = calculatorProcessor.getLastVariable(firstSolutionText) @@ -287,9 +292,7 @@ class CalculatorFragment : Fragment() { var top = mid + delta while (bottom >= min) { - if (Thread.currentThread().id != graphThreadId.get()) { - break - } + yield() drawGraphPoint(firstSolutionText, varStr, bottom) drawGraphPoint(firstSolutionText, varStr, top) @@ -298,9 +301,6 @@ class CalculatorFragment : Fragment() { top += delta } } - - graphThreadId.set(graphThread.id) - graphThread.start() } private fun drawGraphPoint( From d88bf53d69af3e643e5a9b01d9da11e1629cfec2 Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 07/26] Fix GraphGrid --- .../{GraphicFragment.kt => GraphFragment.kt} | 2 +- .../com/fintamath/widget/graph/GraphView.kt | 43 +++++++-------- .../fintamath/widget/graph/grid/GraphGrid.kt | 52 +++++++++++++++++++ .../fintamath/widget/graph/grid/GridLine.kt | 27 ++++++++++ app/src/main/res/navigation/navigation.xml | 2 +- 5 files changed, 100 insertions(+), 26 deletions(-) rename app/src/main/java/com/fintamath/fragment/graphic/{GraphicFragment.kt => GraphFragment.kt} (95%) create mode 100644 app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt create mode 100644 app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt diff --git a/app/src/main/java/com/fintamath/fragment/graphic/GraphicFragment.kt b/app/src/main/java/com/fintamath/fragment/graphic/GraphFragment.kt similarity index 95% rename from app/src/main/java/com/fintamath/fragment/graphic/GraphicFragment.kt rename to app/src/main/java/com/fintamath/fragment/graphic/GraphFragment.kt index fbd14c60..d1c649dc 100644 --- a/app/src/main/java/com/fintamath/fragment/graphic/GraphicFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graphic/GraphFragment.kt @@ -8,7 +8,7 @@ import androidx.fragment.app.Fragment import androidx.navigation.findNavController import com.fintamath.databinding.FragmentGraphicBinding -class GraphicFragment : Fragment() { +class GraphFragment : Fragment() { private lateinit var viewBinding: FragmentGraphicBinding diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt index 3a58d46d..c22f710e 100644 --- a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -8,42 +8,49 @@ import android.graphics.Paint import android.view.MotionEvent import android.util.AttributeSet import android.view.View +import com.fintamath.widget.graph.grid.GraphGrid import kotlin.math.abs -import kotlin.math.min class GraphView( ctx: Context, attrs: AttributeSet ) : View(ctx, attrs) { + private var lockScrollScale = false + + private val axisPaint = Paint().apply { color = Color.LTGRAY strokeWidth = 3f } - private val gridPaint = Paint().apply { - color = Color.DKGRAY - strokeWidth = 2f + private val axisNamePaint = Paint().apply { + color = Color.LTGRAY + textSize = 40f } private var offsetX = 0.0f private var offsetY = 0.0f - private var cellSize: Float = 0.0f private val minCellCount: Int = 10 + private var graphGrid: GraphGrid = GraphGrid() + + override fun onDraw(canvas: Canvas) { super.onDraw(canvas) val width = width.toFloat() val height = height.toFloat() - cellSize = min(height, width) / minCellCount - - drawGrid(canvas) + graphGrid.update(width, height, offsetX, offsetY, minCellCount) + graphGrid.onDraw(canvas) - if (abs(offsetX) < width / 2) { + if ((offsetX <= 0 && abs(offsetX) < (width / 2 - 40f)) || (offsetX > 0 && abs(offsetX) < (width / 2 - 20f))) { drawVerticalAxis(canvas) + canvas.drawText("Y", width / 2 + offsetX - 30f, 50f, axisNamePaint) + } else { + canvas.drawText("Y", (if (offsetX < 0) 10f else width- 40f), 50f, axisNamePaint) } if (abs(offsetY) < height / 2) { @@ -67,23 +74,11 @@ class GraphView( private var lastX = 0f private var lastY = 0f - private fun drawGrid(canvas: Canvas) { - var currentXCoord = offsetX % cellSize - var currentYCoord = offsetY % cellSize - - while (currentXCoord < width.toFloat()) { - canvas.drawLine(currentXCoord, 0.0f, currentXCoord, height.toFloat(), gridPaint) - currentXCoord += cellSize - } - - while (currentYCoord < height.toFloat()) { - canvas.drawLine(0.0f, currentYCoord, width.toFloat(), currentYCoord, gridPaint) - currentYCoord += cellSize - } - } - @SuppressLint("ClickableViewAccessibility") override fun onTouchEvent(event: MotionEvent): Boolean { + if (lockScrollScale) { + return true + } val x = event.x val y = event.y diff --git a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt b/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt new file mode 100644 index 00000000..05e6feb3 --- /dev/null +++ b/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt @@ -0,0 +1,52 @@ +package com.fintamath.widget.graph.grid + +import android.graphics.Canvas +import kotlin.math.min + +class GraphGrid() { + + private var offsetX: Float = 0f + private var offsetY: Float = 0f + + private var width: Float = 0f + private var height: Float = 0f + + private var gridLines : MutableList = ArrayList() + + private var cellSize: Float = 0.0f + + fun onDraw(canvas: Canvas) { + for (gridLine in gridLines) { + gridLine.onDraw(canvas) + } + } + + fun update(newWidth: Float, newHeight: Float, newOffsetX: Float, newOffsetY: Float, cellCount: Int) { + gridLines = ArrayList() + width = newWidth + height = newHeight + cellSize = min(height, width) / cellCount + + offsetX = (newOffsetX + width / 2) % cellSize + offsetY = (newOffsetY + height / 2) % cellSize + addVerticalLines() + addHorizontalLines() + } + + private fun addVerticalLines() { + var xCoord = offsetX + while (xCoord < width) { + gridLines.add(GridLine(width, height, xCoord, 0f, false)) + xCoord += cellSize + } + } + + private fun addHorizontalLines() { + var yCoord = offsetY + while (yCoord < height) { + gridLines.add(GridLine(width, height, 0f, yCoord, true)) + yCoord += cellSize + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt b/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt new file mode 100644 index 00000000..65beec3b --- /dev/null +++ b/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt @@ -0,0 +1,27 @@ +package com.fintamath.widget.graph.grid + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint + +class GridLine( + private val width: Float, + private val height: Float, + private val x: Float, + private val y: Float, + private val isHorizontal: Boolean +) { + + private val gridPaint = Paint().apply { + color = Color.DKGRAY + strokeWidth = 2f + } + + fun onDraw(canvas: Canvas) { + if (isHorizontal) { + canvas.drawLine(0f, y, width, y, gridPaint) + } else { + canvas.drawLine(x, 0f, x, height, gridPaint) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml index 094ab4b4..6cc85562 100644 --- a/app/src/main/res/navigation/navigation.xml +++ b/app/src/main/res/navigation/navigation.xml @@ -77,7 +77,7 @@ tools:layout="@layout/fragment_settings" /> \ No newline at end of file From e400466a8702595f9c0b06bf3455ff021fd0c536 Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 08/26] Rename Graphic to Graph --- .../com/fintamath/fragment/{graphic => graph}/GraphFragment.kt | 2 +- app/src/main/res/layout/fragment_calculator.xml | 3 +-- app/src/main/res/layout/fragment_graphic.xml | 2 +- app/src/main/res/navigation/navigation.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) rename app/src/main/java/com/fintamath/fragment/{graphic => graph}/GraphFragment.kt (95%) diff --git a/app/src/main/java/com/fintamath/fragment/graphic/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt similarity index 95% rename from app/src/main/java/com/fintamath/fragment/graphic/GraphFragment.kt rename to app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index d1c649dc..2aeb11c6 100644 --- a/app/src/main/java/com/fintamath/fragment/graphic/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -1,4 +1,4 @@ -package com.fintamath.fragment.graphic +package com.fintamath.fragment.graph import android.os.Bundle import android.view.LayoutInflater diff --git a/app/src/main/res/layout/fragment_calculator.xml b/app/src/main/res/layout/fragment_calculator.xml index b6f03e3f..9ee4ef62 100644 --- a/app/src/main/res/layout/fragment_calculator.xml +++ b/app/src/main/res/layout/fragment_calculator.xml @@ -72,7 +72,7 @@ android:layout_height="@dimen/diameter_bar_button" android:layout_weight="1" android:background="@drawable/background_bar_button" - android:contentDescription="@string/graphic" + android:contentDescription="@string/graph" android:src="@drawable/ic_history" /> - diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml index 6cc85562..cd29188f 100644 --- a/app/src/main/res/navigation/navigation.xml +++ b/app/src/main/res/navigation/navigation.xml @@ -77,7 +77,7 @@ tools:layout="@layout/fragment_settings" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4dcbc6a7..7ca4e0e3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,7 +5,7 @@ Back Camera History - Graphic + Graph Edit Settings Bookmark From 89aa43c7ad94b82573c4fe313df3c9379f569f95 Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 09/26] Add numeration for Grid --- .../com/fintamath/widget/graph/GraphView.kt | 2 +- .../fintamath/widget/graph/grid/GraphGrid.kt | 38 +++++++++++++++---- .../fintamath/widget/graph/grid/GridLine.kt | 14 ++++++- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt index c22f710e..12d606b0 100644 --- a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -43,7 +43,7 @@ class GraphView( val width = width.toFloat() val height = height.toFloat() - graphGrid.update(width, height, offsetX, offsetY, minCellCount) + graphGrid.update(width, height, offsetX, offsetY, minCellCount, 1f) graphGrid.onDraw(canvas) if ((offsetX <= 0 && abs(offsetX) < (width / 2 - 40f)) || (offsetX > 0 && abs(offsetX) < (width / 2 - 20f))) { diff --git a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt b/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt index 05e6feb3..5de742d2 100644 --- a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt +++ b/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt @@ -14,38 +14,60 @@ class GraphGrid() { private var gridLines : MutableList = ArrayList() private var cellSize: Float = 0.0f + private var cellDelta: Float = 0.0f + private var minimalX: Float = 0.0f + private var minimalY: Float = 0.0f fun onDraw(canvas: Canvas) { for (gridLine in gridLines) { - gridLine.onDraw(canvas) + gridLine.onDraw(canvas, cellSize, cellDelta >= 1) } } - fun update(newWidth: Float, newHeight: Float, newOffsetX: Float, newOffsetY: Float, cellCount: Int) { + fun update(newWidth: Float, + newHeight: Float, + newOffsetX: Float, + newOffsetY: Float, + cellCount: Int, + cellDelta: Float) { gridLines = ArrayList() width = newWidth height = newHeight cellSize = min(height, width) / cellCount - offsetX = (newOffsetX + width / 2) % cellSize - offsetY = (newOffsetY + height / 2) % cellSize + offsetX = newOffsetX + offsetY = newOffsetY + this.cellDelta = cellDelta + this.minimalY = countMinimalY().toFloat() + this.minimalX = countMinimalX().toFloat() addVerticalLines() addHorizontalLines() } + private fun countMinimalX() : Int { + return (( - width / 2 -offsetX) / cellSize).toInt() + } + + private fun countMinimalY() : Int { + return (( offsetY + height / 2 )/ cellSize).toInt() + } private fun addVerticalLines() { - var xCoord = offsetX + var xCoord = (offsetX + width / 2) % cellSize + var value: Float = minimalX while (xCoord < width) { - gridLines.add(GridLine(width, height, xCoord, 0f, false)) + gridLines.add(GridLine(width, height, xCoord, offsetY + height / 2, false, value)) xCoord += cellSize + value += cellDelta } } private fun addHorizontalLines() { - var yCoord = offsetY + var yCoord = (offsetY + height / 2) % cellSize + var value: Float = minimalY while (yCoord < height) { - gridLines.add(GridLine(width, height, 0f, yCoord, true)) + gridLines.add(GridLine(width, height, offsetX + width / 2, yCoord, true, value)) yCoord += cellSize + value -= cellDelta } } diff --git a/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt b/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt index 65beec3b..f1ec515a 100644 --- a/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt +++ b/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt @@ -9,7 +9,8 @@ class GridLine( private val height: Float, private val x: Float, private val y: Float, - private val isHorizontal: Boolean + private val isHorizontal: Boolean, + private val value: Float ) { private val gridPaint = Paint().apply { @@ -17,11 +18,20 @@ class GridLine( strokeWidth = 2f } - fun onDraw(canvas: Canvas) { + private val textPaint = Paint().apply { + color = Color.LTGRAY + textSize = 25f + textAlign = Paint.Align.RIGHT + } + + fun onDraw(canvas: Canvas, cellSize: Float, drawInt : Boolean) { if (isHorizontal) { canvas.drawLine(0f, y, width, y, gridPaint) + if (value != 0f) + canvas.drawText(if (drawInt) value.toInt().toString() else value.toString(), (x - cellSize / 4).coerceIn(cellSize, width - cellSize / 4), y + cellSize / 4, textPaint) } else { canvas.drawLine(x, 0f, x, height, gridPaint) + canvas.drawText(if (drawInt) value.toInt().toString() else value.toString(), x - cellSize / 8, (y + cellSize / 2).coerceIn(cellSize / 2, height - cellSize / 4), textPaint) } } } \ No newline at end of file From 4d1453460f796684073d73850db40311914eeb2a Mon Sep 17 00:00:00 2001 From: ProtasSemyon Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 10/26] Rename Graphic to Graph (cherry picked from commit 29c307f4297724e738dd2e176290b985b25aab0c) --- .../main/res/layout/{fragment_graphic.xml => fragment_graph.xml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/res/layout/{fragment_graphic.xml => fragment_graph.xml} (100%) diff --git a/app/src/main/res/layout/fragment_graphic.xml b/app/src/main/res/layout/fragment_graph.xml similarity index 100% rename from app/src/main/res/layout/fragment_graphic.xml rename to app/src/main/res/layout/fragment_graph.xml From cad1a04080a39d4b345681fa6baa12c7c89a86ec Mon Sep 17 00:00:00 2001 From: ProtasSemyon Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 11/26] Add scale to GraphView (cherry picked from commit 85043c6d7a460aba37e451b6bdc4640c0ff691ad) --- .../fintamath/fragment/graph/GraphFragment.kt | 6 +-- .../com/fintamath/widget/graph/GraphView.kt | 52 +++++++++++++++---- .../fintamath/widget/graph/grid/GraphGrid.kt | 4 +- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index 2aeb11c6..312e7add 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -6,17 +6,17 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.findNavController -import com.fintamath.databinding.FragmentGraphicBinding +import com.fintamath.databinding.FragmentGraphBinding class GraphFragment : Fragment() { - private lateinit var viewBinding: FragmentGraphicBinding + private lateinit var viewBinding: FragmentGraphBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { - viewBinding = FragmentGraphicBinding.inflate(inflater, container, false) + viewBinding = FragmentGraphBinding.inflate(inflater, container, false) viewBinding.aboutBackButton.setOnClickListener { executeBack() } diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt index 12d606b0..f6f48c90 100644 --- a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -7,6 +7,7 @@ import android.graphics.Color import android.graphics.Paint import android.view.MotionEvent import android.util.AttributeSet +import android.view.ScaleGestureDetector import android.view.View import com.fintamath.widget.graph.grid.GraphGrid import kotlin.math.abs @@ -24,17 +25,14 @@ class GraphView( strokeWidth = 3f } - private val axisNamePaint = Paint().apply { - color = Color.LTGRAY - textSize = 40f - } - private var offsetX = 0.0f private var offsetY = 0.0f private val minCellCount: Int = 10 + private var currCellCount = minCellCount private var graphGrid: GraphGrid = GraphGrid() + private var cellDelta = 1f override fun onDraw(canvas: Canvas) { @@ -43,14 +41,11 @@ class GraphView( val width = width.toFloat() val height = height.toFloat() - graphGrid.update(width, height, offsetX, offsetY, minCellCount, 1f) + graphGrid.update(width, height, offsetX, offsetY, currCellCount, cellDelta) graphGrid.onDraw(canvas) if ((offsetX <= 0 && abs(offsetX) < (width / 2 - 40f)) || (offsetX > 0 && abs(offsetX) < (width / 2 - 20f))) { drawVerticalAxis(canvas) - canvas.drawText("Y", width / 2 + offsetX - 30f, 50f, axisNamePaint) - } else { - canvas.drawText("Y", (if (offsetX < 0) 10f else width- 40f), 50f, axisNamePaint) } if (abs(offsetY) < height / 2) { @@ -71,6 +66,41 @@ class GraphView( canvas.drawLine(offsetWidth, 0f, offsetWidth, height, axisPaint) } + private var mScaleFactor = 1f + + private val scaleListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() { + + override fun onScale(detector: ScaleGestureDetector): Boolean { + mScaleFactor /= detector.scaleFactor + + println(mScaleFactor) + + updateScale() + + + invalidate() + return true + } + } + + private fun updateScale() { + if (mScaleFactor < 0.5f) { + mScaleFactor = 1f + cellDelta /=2 + } + + if (mScaleFactor > 1.5f) { + mScaleFactor = 1f + cellDelta *= 2 + + } + + currCellCount = (minCellCount * mScaleFactor).toInt() + } + + private val mScaleDetector = ScaleGestureDetector(context, scaleListener) + + private var lastX = 0f private var lastY = 0f @@ -79,6 +109,10 @@ class GraphView( if (lockScrollScale) { return true } + if (mScaleDetector.onTouchEvent(event)) { + return true + } + val x = event.x val y = event.y diff --git a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt b/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt index 5de742d2..7f222d73 100644 --- a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt +++ b/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt @@ -53,7 +53,7 @@ class GraphGrid() { } private fun addVerticalLines() { var xCoord = (offsetX + width / 2) % cellSize - var value: Float = minimalX + var value: Float = minimalX * cellDelta while (xCoord < width) { gridLines.add(GridLine(width, height, xCoord, offsetY + height / 2, false, value)) xCoord += cellSize @@ -63,7 +63,7 @@ class GraphGrid() { private fun addHorizontalLines() { var yCoord = (offsetY + height / 2) % cellSize - var value: Float = minimalY + var value: Float = minimalY * cellDelta while (yCoord < height) { gridLines.add(GridLine(width, height, offsetX + width / 2, yCoord, true, value)) yCoord += cellSize From ecc31d3340b04e2fa3334d3b7112c2b9fabb2c39 Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 12/26] Rename CalculatorInputStorage to CalculatorStorage --- .../fragment/calculator/CalculatorFragment.kt | 11 ++++++----- .../com/fintamath/fragment/history/HistoryFragment.kt | 7 ++----- .../com/fintamath/storage/CalculatorInputStorage.kt | 5 ----- .../java/com/fintamath/storage/CalculatorStorage.kt | 6 ++++++ 4 files changed, 14 insertions(+), 15 deletions(-) delete mode 100644 app/src/main/java/com/fintamath/storage/CalculatorInputStorage.kt create mode 100644 app/src/main/java/com/fintamath/storage/CalculatorStorage.kt diff --git a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt index 502d76e5..a21977d9 100644 --- a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt @@ -14,7 +14,7 @@ import com.fintamath.R import com.fintamath.calculator.CalculatorProcessor import com.fintamath.databinding.FragmentCalculatorBinding import com.fintamath.storage.HistoryStorage -import com.fintamath.storage.CalculatorInputStorage +import com.fintamath.storage.CalculatorStorage import com.fintamath.storage.MathTextData import com.fintamath.storage.SettingsStorage import com.fintamath.widget.keyboard.Keyboard @@ -86,8 +86,8 @@ class CalculatorFragment : Fragment() { updateSettings() if (inTextViewState.get() == InTextViewState.Ready.value) { - if (viewBinding.inTextView.text != CalculatorInputStorage.mathTextData.text) { - viewBinding.inTextView.text = CalculatorInputStorage.mathTextData.text + if (viewBinding.inTextView.text != CalculatorStorage.inputMathTextData.text) { + viewBinding.inTextView.text = CalculatorStorage.inputMathTextData.text } if (viewBinding.outSolutionView.isShowingLoading() || wereSettingsUpdated.get()) { @@ -111,7 +111,7 @@ class CalculatorFragment : Fragment() { private fun initMathTexts() { viewBinding.inTextLayout.setOnTouchListener { _, event -> touchInText(event) } - viewBinding.inTextView.text = CalculatorInputStorage.mathTextData.text + viewBinding.inTextView.text = CalculatorStorage.inputMathTextData.text viewBinding.inTextView.setOnTextChangedListener { _, text -> onInTextChange(text) } viewBinding.inTextView.setOnFocusChangeListener { _, state -> onInTextFocusChange(state) } } @@ -215,7 +215,7 @@ class CalculatorFragment : Fragment() { } } - CalculatorInputStorage.mathTextData = MathTextData(text) + CalculatorStorage.inputMathTextData = MathTextData(text) cancelSaveToHistoryTask() viewBinding.inTextViewHint.visibility = if (viewBinding.inTextView.text.isNotEmpty()) @@ -255,6 +255,7 @@ class CalculatorFragment : Fragment() { } else { val cutSolutionTexts = cutSolutionTexts(texts) val firstSolutionText = cutSolutionTexts.first() + CalculatorStorage.firstOutputMathTextData.text = firstSolutionText if (countTextsLength(cutSolutionTexts) > maxSolutionLength) { viewBinding.outSolutionView.showCharacterLimitExceeded() diff --git a/app/src/main/java/com/fintamath/fragment/history/HistoryFragment.kt b/app/src/main/java/com/fintamath/fragment/history/HistoryFragment.kt index 41c37933..f934fb59 100644 --- a/app/src/main/java/com/fintamath/fragment/history/HistoryFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/history/HistoryFragment.kt @@ -13,10 +13,7 @@ import com.fintamath.R import com.fintamath.databinding.FragmentHistoryBinding import com.fintamath.storage.HistoryStorage import com.fintamath.storage.MathTextData -import com.fintamath.storage.CalculatorInputStorage -import java.util.Timer -import java.util.TimerTask -import kotlin.concurrent.schedule +import com.fintamath.storage.CalculatorStorage class HistoryFragment : Fragment() { @@ -73,7 +70,7 @@ class HistoryFragment : Fragment() { } private fun onCalculate(text: String) { - CalculatorInputStorage.mathTextData = MathTextData(text) + CalculatorStorage.inputMathTextData = MathTextData(text) showCalculatorFragment() } diff --git a/app/src/main/java/com/fintamath/storage/CalculatorInputStorage.kt b/app/src/main/java/com/fintamath/storage/CalculatorInputStorage.kt deleted file mode 100644 index 8bf2cd6c..00000000 --- a/app/src/main/java/com/fintamath/storage/CalculatorInputStorage.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.fintamath.storage - -object CalculatorInputStorage { - var mathTextData: MathTextData = MathTextData("") -} diff --git a/app/src/main/java/com/fintamath/storage/CalculatorStorage.kt b/app/src/main/java/com/fintamath/storage/CalculatorStorage.kt new file mode 100644 index 00000000..aa3a30bb --- /dev/null +++ b/app/src/main/java/com/fintamath/storage/CalculatorStorage.kt @@ -0,0 +1,6 @@ +package com.fintamath.storage + +object CalculatorStorage { + var inputMathTextData: MathTextData = MathTextData("") + var firstOutputMathTextData: MathTextData = MathTextData("") +} From 0fe1872900d54b29cec06aeea929c2ee709026ff Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 13/26] Add graphs approximator --- .../fintamath/approximator/Approximator.kt | 14 +++++++ lib/src/Fintamath.cpp | 39 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 app/src/main/java/com/fintamath/approximator/Approximator.kt diff --git a/app/src/main/java/com/fintamath/approximator/Approximator.kt b/app/src/main/java/com/fintamath/approximator/Approximator.kt new file mode 100644 index 00000000..c18756e2 --- /dev/null +++ b/app/src/main/java/com/fintamath/approximator/Approximator.kt @@ -0,0 +1,14 @@ +package com.fintamath.approximator + +class Approximator { + external fun approximate(exprStr: String, varStr: String, valStr: String): String + + external fun getVariableCount(exprStr: String): Int + + external fun getLastVariable(exprStr: String): String + companion object { + init { + System.loadLibrary("fintamath_android") + } + } +} \ No newline at end of file diff --git a/lib/src/Fintamath.cpp b/lib/src/Fintamath.cpp index bda4fd7e..10f20663 100644 --- a/lib/src/Fintamath.cpp +++ b/lib/src/Fintamath.cpp @@ -177,3 +177,42 @@ extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Calculator_getLastVar return env->NewStringUTF(exprVariablesCached.back().toString().c_str()); } + +extern "C" +JNIEXPORT jstring +Java_com_fintamath_approximator_Approximator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { + std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); + std::string varStr = env->GetStringUTFChars(varJStr, nullptr); + std::string valStr = env->GetStringUTFChars(valJStr, nullptr); + + updateCachedExpression(exprStr); + + Expression approxExpr = exprCached; + approxExpr.setVariable(Variable(varStr), Real(valStr)); + approxExpr = approxExpr.approximate(); + + if (auto res = cast(approxExpr.toMinimalObject())) { + return env->NewStringUTF(res->toString(5).c_str()); + } + + return env->NewStringUTF(""); +} + +extern "C" JNIEXPORT jint Java_com_fintamath_approximator_Approximator_getVariableCount(JNIEnv *env, jobject instance, jstring exprJStr) { + std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); + updateCachedExpression(exprStr); + return jint(exprVariablesCached.size()); +} + +extern "C" JNIEXPORT jstring Java_com_fintamath_approximator_Approximator_getLastVariable(JNIEnv *env, jobject instance, jstring exprJStr) { + std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); + updateCachedExpression(exprStr); + + if (exprVariablesCached.empty()) { + return env->NewStringUTF(""); + } + + return env->NewStringUTF(exprVariablesCached.back().toString().c_str()); +} + + From aad474318a04b934303eff315b5cd9f2b6d94a72 Mon Sep 17 00:00:00 2001 From: Saymon Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 14/26] Add graphs drawing logic --- .../fintamath/fragment/graph/GraphFragment.kt | 76 ++++++- .../com/fintamath/widget/graph/DataPoint.kt | 6 - .../com/fintamath/widget/graph/GraphView.kt | 193 ++++++++++++------ .../fintamath/widget/graph/grid/GraphGrid.kt | 4 + app/src/main/res/layout/fragment_graph.xml | 1 + 5 files changed, 214 insertions(+), 66 deletions(-) delete mode 100644 app/src/main/java/com/fintamath/widget/graph/DataPoint.kt diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index 312e7add..5c495aa5 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -5,25 +5,99 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController +import com.fintamath.R +import com.fintamath.approximator.Approximator import com.fintamath.databinding.FragmentGraphBinding +import com.fintamath.storage.CalculatorStorage +import com.fintamath.widget.graph.GraphView +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.yield +import java.math.BigDecimal class GraphFragment : Fragment() { private lateinit var viewBinding: FragmentGraphBinding + private lateinit var graphView: GraphView + + private var currentFun = CalculatorStorage.firstOutputMathTextData.text override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { viewBinding = FragmentGraphBinding.inflate(inflater, container, false) - viewBinding.aboutBackButton.setOnClickListener { executeBack() } + graphView = viewBinding.root.findViewById(R.id.graphView) + graphView.setOnScrollOrScale { + if (currentFun != CalculatorStorage.firstOutputMathTextData.text) { + currentFun = CalculatorStorage.firstOutputMathTextData.text + graphView.clearGraph() + } + drawGraph(graphView.getMinX(), graphView.getMaxX()) + //drawGraph(graphView.getMinX(), graphView.getMaxX()) + } + drawGraph(BigDecimal(-10), BigDecimal(10)) + return viewBinding.root } private fun executeBack() { viewBinding.root.findNavController().navigateUp() } + + private var drawGraphJob: Job? = null + private val approximator = Approximator() + + private fun drawGraph(minX: BigDecimal, maxX: BigDecimal) { + drawGraph(CalculatorStorage.firstOutputMathTextData.text, minX, maxX) + } + + private fun drawGraph(firstSolutionText: String, min: BigDecimal, max: BigDecimal) { + drawGraphJob?.cancel() + + drawGraphJob = viewLifecycleOwner.lifecycleScope.launch { + if (approximator.getVariableCount(firstSolutionText) != 1) { + return@launch + } + + val varStr = approximator.getLastVariable(firstSolutionText) + val mid = (min + max) / BigDecimal(2) + + drawGraphPoint(firstSolutionText, varStr, mid) + + val delta = (max - min) / BigDecimal(200) + var bottom = mid - delta + var top = mid + delta + + while (bottom >= min) { + yield() + + drawGraphPoint(firstSolutionText, varStr, bottom) + drawGraphPoint(firstSolutionText, varStr, top) + + bottom -= delta + top += delta + } + } + } + + private fun drawGraphPoint( + firstSolutionText: String, + varStr: String, + top: BigDecimal + ) { + graphView.addPoint(top, + BigDecimal(approximator.approximate( + firstSolutionText, + varStr, + top.toString() + ))) + + } + + } diff --git a/app/src/main/java/com/fintamath/widget/graph/DataPoint.kt b/app/src/main/java/com/fintamath/widget/graph/DataPoint.kt deleted file mode 100644 index 58ef2e8e..00000000 --- a/app/src/main/java/com/fintamath/widget/graph/DataPoint.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.fintamath.widget.graph - -data class DataPoint( - val xVal: Int, - val yVal: Int -) \ No newline at end of file diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt index f6f48c90..703e4136 100644 --- a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -7,11 +7,14 @@ import android.graphics.Color import android.graphics.Paint import android.view.MotionEvent import android.util.AttributeSet +import android.view.GestureDetector import android.view.ScaleGestureDetector import android.view.View import com.fintamath.widget.graph.grid.GraphGrid +import java.math.BigDecimal import kotlin.math.abs +@SuppressLint("ClickableViewAccessibility") class GraphView( ctx: Context, attrs: AttributeSet @@ -19,22 +22,103 @@ class GraphView( private var lockScrollScale = false + private var points : MutableMap = mutableMapOf() private val axisPaint = Paint().apply { color = Color.LTGRAY strokeWidth = 3f } + private val pointPaint = Paint().apply { + color = Color.LTGRAY + strokeWidth = 1f + } + private var offsetX = 0.0f private var offsetY = 0.0f private val minCellCount: Int = 10 private var currCellCount = minCellCount + private var updateLambda: ()->Unit = {} + + fun setOnScrollOrScale(onScrollOnScale: ()->Unit) { + updateLambda = onScrollOnScale + println("cleared") + } + + fun clearGraph() { + points.clear() + } + + fun addPoint(x: BigDecimal, y: BigDecimal) { + points[x] = y + invalidate() + } + + private fun onScrollOrScale() { + updateLambda() + } + private var graphGrid: GraphGrid = GraphGrid() private var cellDelta = 1f + private val scrollDetector = GestureDetector(ctx, object : GestureDetector.SimpleOnGestureListener() { + override fun onScroll( + e1: MotionEvent?, + e2: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + if (e1 == null) { + return false + } + when (e2.action) { + MotionEvent.ACTION_MOVE -> { + offsetX -= distanceX + offsetY -= distanceY + } + } + onScrollOrScale() + invalidate() + return true + } + }) + private val scaleDetector = ScaleGestureDetector(ctx, object : ScaleGestureDetector.SimpleOnScaleGestureListener() { + override fun onScale(detector: ScaleGestureDetector): Boolean { + mScaleFactor /= detector.scaleFactor + + updateScale() + onScrollOrScale() + invalidate() + return true + } + }) + + private var mScaleFactor = 1f + + private fun updateScale() { + if (mScaleFactor < 0.5f) { + mScaleFactor = 1f + cellDelta /=2 + } + + if (mScaleFactor > 1.5f) { + mScaleFactor = 1f + cellDelta *= 2 + + } + + currCellCount = (minCellCount * mScaleFactor).toInt() + } + + init { + setOnTouchListener {_, event -> + scrollDetector.onTouchEvent(event) + scaleDetector.onTouchEvent(event) + } + } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) @@ -52,6 +136,7 @@ class GraphView( drawHorizontalAxis(canvas) } + drawPoints(canvas) } private fun drawHorizontalAxis(canvas: Canvas) { @@ -66,73 +151,63 @@ class GraphView( canvas.drawLine(offsetWidth, 0f, offsetWidth, height, axisPaint) } - private var mScaleFactor = 1f - - private val scaleListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() { - - override fun onScale(detector: ScaleGestureDetector): Boolean { - mScaleFactor /= detector.scaleFactor - - println(mScaleFactor) - - updateScale() - - - invalidate() - return true + private fun drawPoints(canvas: Canvas) { + var oldX: BigDecimal? = null + var oldY: BigDecimal? = null + for (x in points.keys.toList().sorted()) { + val y = points[x] + + if (oldX == null) { + oldX = x + oldY = y + if (y != null) { + drawPointOnCanvas(x, y, canvas) + } + continue + } + if (y != null && oldY != null) { + val delta = abs(oldY.toFloat() - y.toFloat()) * graphGrid.getCellSize() / cellDelta + if (delta > height) { + oldX = x + oldY = y + continue + } + drawLineOnCanvas(x, y, oldX, oldY, canvas) + oldX = x + oldY = y + } } } - private fun updateScale() { - if (mScaleFactor < 0.5f) { - mScaleFactor = 1f - cellDelta /=2 - } - - if (mScaleFactor > 1.5f) { - mScaleFactor = 1f - cellDelta *= 2 - - } - - currCellCount = (minCellCount * mScaleFactor).toInt() + private fun drawPointOnCanvas(x: BigDecimal, y: BigDecimal, canvas: Canvas) { + val cellSize = graphGrid.getCellSize() + canvas.drawPoint(offsetX + width/2 + x.toFloat() * cellSize / cellDelta, + offsetY + height/2 - y.toFloat() * cellSize / cellDelta, pointPaint) } - private val mScaleDetector = ScaleGestureDetector(context, scaleListener) - - - private var lastX = 0f - private var lastY = 0f - - @SuppressLint("ClickableViewAccessibility") - override fun onTouchEvent(event: MotionEvent): Boolean { - if (lockScrollScale) { - return true - } - if (mScaleDetector.onTouchEvent(event)) { - return true - } + private fun drawLineOnCanvas(x1: BigDecimal, y1:BigDecimal, x2:BigDecimal, y2:BigDecimal, canvas: Canvas) { + val cellSize = graphGrid.getCellSize() + canvas.drawLine(offsetX + width/2 + x1.toFloat() * cellSize / cellDelta, + offsetY + height/2 - y1.toFloat() * cellSize / cellDelta, + offsetX + width/2 + x2.toFloat() * cellSize / cellDelta, + offsetY + height/2 - y2.toFloat() * cellSize / cellDelta, + pointPaint) + } - val x = event.x - val y = event.y + fun getMinX(): BigDecimal { + return canvasXToGraphX(BigDecimal(0)) + } - when (event.action) { - MotionEvent.ACTION_DOWN -> { - lastX = x - lastY = y - } - MotionEvent.ACTION_MOVE -> { - val deltaX = x - lastX - val deltaY = y - lastY + fun getMaxX() : BigDecimal { + return canvasXToGraphX(BigDecimal(width)) + } - offsetX += deltaX - offsetY += deltaY + private fun canvasXToGraphX(canvasX: BigDecimal) : BigDecimal { + return BigDecimal((cellDelta/graphGrid.getCellSize() * (canvasX.toFloat() - offsetX - width/2)).toString()) + } - lastX = x - lastY = y - } - } - invalidate() - return true + private fun graphXToCanvasX(graphX: BigDecimal) : BigDecimal{ + return BigDecimal((offsetX + width/2 + graphX.toFloat() * graphGrid.getCellSize() / cellDelta).toString()) } + } \ No newline at end of file diff --git a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt b/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt index 7f222d73..ca7cd7df 100644 --- a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt +++ b/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt @@ -44,6 +44,10 @@ class GraphGrid() { addHorizontalLines() } + fun getCellSize(): Float { + return cellSize + } + private fun countMinimalX() : Int { return (( - width / 2 -offsetX) / cellSize).toInt() } diff --git a/app/src/main/res/layout/fragment_graph.xml b/app/src/main/res/layout/fragment_graph.xml index aef5a843..567f6c94 100644 --- a/app/src/main/res/layout/fragment_graph.xml +++ b/app/src/main/res/layout/fragment_graph.xml @@ -51,6 +51,7 @@ android:orientation="vertical"> From e48e37d6f61ba9bb9509473a020093093dc453ab Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 15/26] Renaming & refactoring --- .../Approximator.kt | 4 +- .../fragment/calculator/CalculatorFragment.kt | 2 +- .../fintamath/fragment/graph/GraphFragment.kt | 10 +-- .../fintamath/storage/CalculatorStorage.kt | 2 +- .../widget/graph/{grid => }/GraphGrid.kt | 29 ++++---- .../com/fintamath/widget/graph/GraphView.kt | 72 ++++++++++--------- .../widget/graph/{grid => }/GridLine.kt | 2 +- lib/src/Fintamath.cpp | 6 +- 8 files changed, 68 insertions(+), 59 deletions(-) rename app/src/main/java/com/fintamath/{approximator => calculator}/Approximator.kt (90%) rename app/src/main/java/com/fintamath/widget/graph/{grid => }/GraphGrid.kt (77%) rename app/src/main/java/com/fintamath/widget/graph/{grid => }/GridLine.kt (96%) diff --git a/app/src/main/java/com/fintamath/approximator/Approximator.kt b/app/src/main/java/com/fintamath/calculator/Approximator.kt similarity index 90% rename from app/src/main/java/com/fintamath/approximator/Approximator.kt rename to app/src/main/java/com/fintamath/calculator/Approximator.kt index c18756e2..9ab0a350 100644 --- a/app/src/main/java/com/fintamath/approximator/Approximator.kt +++ b/app/src/main/java/com/fintamath/calculator/Approximator.kt @@ -1,11 +1,13 @@ -package com.fintamath.approximator +package com.fintamath.calculator class Approximator { + external fun approximate(exprStr: String, varStr: String, valStr: String): String external fun getVariableCount(exprStr: String): Int external fun getLastVariable(exprStr: String): String + companion object { init { System.loadLibrary("fintamath_android") diff --git a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt index a21977d9..6951b413 100644 --- a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt @@ -255,7 +255,7 @@ class CalculatorFragment : Fragment() { } else { val cutSolutionTexts = cutSolutionTexts(texts) val firstSolutionText = cutSolutionTexts.first() - CalculatorStorage.firstOutputMathTextData.text = firstSolutionText + CalculatorStorage.outputMathTextData.text = firstSolutionText if (countTextsLength(cutSolutionTexts) > maxSolutionLength) { viewBinding.outSolutionView.showCharacterLimitExceeded() diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index 5c495aa5..a3dc89a5 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -8,7 +8,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import com.fintamath.R -import com.fintamath.approximator.Approximator +import com.fintamath.calculator.Approximator import com.fintamath.databinding.FragmentGraphBinding import com.fintamath.storage.CalculatorStorage import com.fintamath.widget.graph.GraphView @@ -22,7 +22,7 @@ class GraphFragment : Fragment() { private lateinit var viewBinding: FragmentGraphBinding private lateinit var graphView: GraphView - private var currentFun = CalculatorStorage.firstOutputMathTextData.text + private var currentFun = CalculatorStorage.outputMathTextData.text override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -33,8 +33,8 @@ class GraphFragment : Fragment() { graphView = viewBinding.root.findViewById(R.id.graphView) graphView.setOnScrollOrScale { - if (currentFun != CalculatorStorage.firstOutputMathTextData.text) { - currentFun = CalculatorStorage.firstOutputMathTextData.text + if (currentFun != CalculatorStorage.outputMathTextData.text) { + currentFun = CalculatorStorage.outputMathTextData.text graphView.clearGraph() } drawGraph(graphView.getMinX(), graphView.getMaxX()) @@ -53,7 +53,7 @@ class GraphFragment : Fragment() { private val approximator = Approximator() private fun drawGraph(minX: BigDecimal, maxX: BigDecimal) { - drawGraph(CalculatorStorage.firstOutputMathTextData.text, minX, maxX) + drawGraph(CalculatorStorage.outputMathTextData.text, minX, maxX) } private fun drawGraph(firstSolutionText: String, min: BigDecimal, max: BigDecimal) { diff --git a/app/src/main/java/com/fintamath/storage/CalculatorStorage.kt b/app/src/main/java/com/fintamath/storage/CalculatorStorage.kt index aa3a30bb..2788dd16 100644 --- a/app/src/main/java/com/fintamath/storage/CalculatorStorage.kt +++ b/app/src/main/java/com/fintamath/storage/CalculatorStorage.kt @@ -2,5 +2,5 @@ package com.fintamath.storage object CalculatorStorage { var inputMathTextData: MathTextData = MathTextData("") - var firstOutputMathTextData: MathTextData = MathTextData("") + var outputMathTextData: MathTextData = MathTextData("") } diff --git a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt b/app/src/main/java/com/fintamath/widget/graph/GraphGrid.kt similarity index 77% rename from app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt rename to app/src/main/java/com/fintamath/widget/graph/GraphGrid.kt index ca7cd7df..05d30792 100644 --- a/app/src/main/java/com/fintamath/widget/graph/grid/GraphGrid.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphGrid.kt @@ -1,4 +1,4 @@ -package com.fintamath.widget.graph.grid +package com.fintamath.widget.graph import android.graphics.Canvas import kotlin.math.min @@ -24,22 +24,24 @@ class GraphGrid() { } } - fun update(newWidth: Float, - newHeight: Float, - newOffsetX: Float, - newOffsetY: Float, + fun update(width: Float, + height: Float, + offsetX: Float, + offsetY: Float, cellCount: Int, cellDelta: Float) { - gridLines = ArrayList() - width = newWidth - height = newHeight - cellSize = min(height, width) / cellCount - offsetX = newOffsetX - offsetY = newOffsetY + this.gridLines = ArrayList() + this.width = width + this.height = height + this.cellSize = min(this.height, this.width) / cellCount + + this.offsetX = offsetX + this.offsetY = offsetY this.cellDelta = cellDelta this.minimalY = countMinimalY().toFloat() this.minimalX = countMinimalX().toFloat() + addVerticalLines() addHorizontalLines() } @@ -49,12 +51,13 @@ class GraphGrid() { } private fun countMinimalX() : Int { - return (( - width / 2 -offsetX) / cellSize).toInt() + return ((-width / 2 - offsetX) / cellSize).toInt() } private fun countMinimalY() : Int { - return (( offsetY + height / 2 )/ cellSize).toInt() + return ((offsetY + height / 2 ) / cellSize).toInt() } + private fun addVerticalLines() { var xCoord = (offsetX + width / 2) % cellSize var value: Float = minimalX * cellDelta diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt index 703e4136..c7357a8d 100644 --- a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -10,7 +10,6 @@ import android.util.AttributeSet import android.view.GestureDetector import android.view.ScaleGestureDetector import android.view.View -import com.fintamath.widget.graph.grid.GraphGrid import java.math.BigDecimal import kotlin.math.abs @@ -42,26 +41,9 @@ class GraphView( private var updateLambda: ()->Unit = {} - fun setOnScrollOrScale(onScrollOnScale: ()->Unit) { - updateLambda = onScrollOnScale - println("cleared") - } - - fun clearGraph() { - points.clear() - } - - fun addPoint(x: BigDecimal, y: BigDecimal) { - points[x] = y - invalidate() - } - - private fun onScrollOrScale() { - updateLambda() - } - private var graphGrid: GraphGrid = GraphGrid() private var cellDelta = 1f + private var scaleFactor = 1f private val scrollDetector = GestureDetector(ctx, object : GestureDetector.SimpleOnGestureListener() { override fun onScroll( @@ -70,55 +52,76 @@ class GraphView( distanceX: Float, distanceY: Float ): Boolean { + if (e1 == null) { return false } + when (e2.action) { MotionEvent.ACTION_MOVE -> { offsetX -= distanceX offsetY -= distanceY } } + onScrollOrScale() invalidate() + return true } }) + private val scaleDetector = ScaleGestureDetector(ctx, object : ScaleGestureDetector.SimpleOnScaleGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { - mScaleFactor /= detector.scaleFactor + scaleFactor /= detector.scaleFactor updateScale() onScrollOrScale() invalidate() + return true } }) - private var mScaleFactor = 1f + init { + setOnTouchListener {_, event -> + scrollDetector.onTouchEvent(event) + scaleDetector.onTouchEvent(event) + } + } + + fun setOnScrollOrScale(onScrollOnScale: ()->Unit) { + updateLambda = onScrollOnScale + } + + fun clearGraph() { + points.clear() + } + + fun addPoint(x: BigDecimal, y: BigDecimal) { + points[x] = y + invalidate() + } + + private fun onScrollOrScale() { + updateLambda() + } private fun updateScale() { - if (mScaleFactor < 0.5f) { - mScaleFactor = 1f + if (scaleFactor < 0.5f) { + scaleFactor = 1f cellDelta /=2 } - if (mScaleFactor > 1.5f) { - mScaleFactor = 1f + if (scaleFactor > 1.5f) { + scaleFactor = 1f cellDelta *= 2 } - currCellCount = (minCellCount * mScaleFactor).toInt() + currCellCount = (minCellCount * scaleFactor).toInt() } - init { - setOnTouchListener {_, event -> - scrollDetector.onTouchEvent(event) - scaleDetector.onTouchEvent(event) - } - - } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) @@ -154,6 +157,7 @@ class GraphView( private fun drawPoints(canvas: Canvas) { var oldX: BigDecimal? = null var oldY: BigDecimal? = null + for (x in points.keys.toList().sorted()) { val y = points[x] @@ -182,7 +186,7 @@ class GraphView( private fun drawPointOnCanvas(x: BigDecimal, y: BigDecimal, canvas: Canvas) { val cellSize = graphGrid.getCellSize() canvas.drawPoint(offsetX + width/2 + x.toFloat() * cellSize / cellDelta, - offsetY + height/2 - y.toFloat() * cellSize / cellDelta, pointPaint) + offsetY + height/2 - y.toFloat() * cellSize / cellDelta, pointPaint) } private fun drawLineOnCanvas(x1: BigDecimal, y1:BigDecimal, x2:BigDecimal, y2:BigDecimal, canvas: Canvas) { diff --git a/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt b/app/src/main/java/com/fintamath/widget/graph/GridLine.kt similarity index 96% rename from app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt rename to app/src/main/java/com/fintamath/widget/graph/GridLine.kt index f1ec515a..3635c4cc 100644 --- a/app/src/main/java/com/fintamath/widget/graph/grid/GridLine.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GridLine.kt @@ -1,4 +1,4 @@ -package com.fintamath.widget.graph.grid +package com.fintamath.widget.graph import android.graphics.Canvas import android.graphics.Color diff --git a/lib/src/Fintamath.cpp b/lib/src/Fintamath.cpp index 10f20663..ad36ac2a 100644 --- a/lib/src/Fintamath.cpp +++ b/lib/src/Fintamath.cpp @@ -180,7 +180,7 @@ extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Calculator_getLastVar extern "C" JNIEXPORT jstring -Java_com_fintamath_approximator_Approximator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { +Java_com_fintamath_calculator_Approximator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); std::string varStr = env->GetStringUTFChars(varJStr, nullptr); std::string valStr = env->GetStringUTFChars(valJStr, nullptr); @@ -198,13 +198,13 @@ Java_com_fintamath_approximator_Approximator_approximate(JNIEnv *env, jobject in return env->NewStringUTF(""); } -extern "C" JNIEXPORT jint Java_com_fintamath_approximator_Approximator_getVariableCount(JNIEnv *env, jobject instance, jstring exprJStr) { +extern "C" JNIEXPORT jint Java_com_fintamath_calculator_Approximator_getVariableCount(JNIEnv *env, jobject instance, jstring exprJStr) { std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); updateCachedExpression(exprStr); return jint(exprVariablesCached.size()); } -extern "C" JNIEXPORT jstring Java_com_fintamath_approximator_Approximator_getLastVariable(JNIEnv *env, jobject instance, jstring exprJStr) { +extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Approximator_getLastVariable(JNIEnv *env, jobject instance, jstring exprJStr) { std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); updateCachedExpression(exprStr); From d6a08bc210467a0cf52b72419d308fec948ec395 Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 16/26] Rename Graphic to Graph - part 2 --- .../fragment/calculator/CalculatorFragment.kt | 8 ++++---- app/src/main/res/layout/fragment_calculator.xml | 2 +- app/src/main/res/navigation/navigation.xml | 15 +++++++++------ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt index 6951b413..2b887081 100644 --- a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt @@ -182,11 +182,11 @@ class CalculatorFragment : Fragment() { } private fun initBarButtons() { -// viewBinding.cameraButton.setOnClickListener { showCameraFragment() } // TODO: uncomment when camera is implemented + // viewBinding.cameraButton.setOnClickListener { showCameraFragment() } // TODO: uncomment when camera is implemented viewBinding.historyButton.setOnClickListener { showHistoryFragment() } viewBinding.settingsButton.setOnClickListener { showSettingsFragment() } viewBinding.aboutButton.setOnClickListener { showAboutFragment() } - viewBinding.graphicButton.setOnClickListener { showGraphicFragment() } + viewBinding.graphButton.setOnClickListener { showGraphFragment() } } private fun updateSettings() { @@ -373,8 +373,8 @@ class CalculatorFragment : Fragment() { showFragment(R.id.action_calculatorFragment_to_settingsFragment) } - private fun showGraphicFragment() { - showFragment(R.id.action_calculatorFragment_to_graphicFragment) + private fun showGraphFragment() { + showFragment(R.id.action_calculatorFragment_to_graphFragment) } private fun showFragment(navigationId: Int) { diff --git a/app/src/main/res/layout/fragment_calculator.xml b/app/src/main/res/layout/fragment_calculator.xml index 9ee4ef62..fb312420 100644 --- a/app/src/main/res/layout/fragment_calculator.xml +++ b/app/src/main/res/layout/fragment_calculator.xml @@ -67,7 +67,7 @@ android:layout_height="wrap_content" /> + + android:id="@+id/action_calculatorFragment_to_graphFragment" + app:destination="@id/graphFragment" /> @@ -64,6 +65,12 @@ + + - \ No newline at end of file From 8d15aca9ce306ea1ac48debab35446fc0ef2f943 Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 17/26] Improve navigation with graph fragment --- .../fintamath/fragment/graph/GraphFragment.kt | 45 +++++++++++-- .../fragment/history/HistoryFragment.kt | 8 ++- app/src/main/res/drawable/ic_graph.xml | 5 ++ .../main/res/layout/fragment_calculator.xml | 23 ++++--- app/src/main/res/layout/fragment_graph.xml | 64 ++++++++++++++++--- app/src/main/res/layout/fragment_history.xml | 12 ++++ app/src/main/res/navigation/navigation.xml | 20 ++++-- 7 files changed, 145 insertions(+), 32 deletions(-) create mode 100644 app/src/main/res/drawable/ic_graph.xml diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index a3dc89a5..90c6cba3 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -29,24 +29,32 @@ class GraphFragment : Fragment() { savedInstanceState: Bundle?, ): View { viewBinding = FragmentGraphBinding.inflate(inflater, container, false) - viewBinding.aboutBackButton.setOnClickListener { executeBack() } + + initBarButtons() graphView = viewBinding.root.findViewById(R.id.graphView) + graphView.setOnScrollOrScale { if (currentFun != CalculatorStorage.outputMathTextData.text) { currentFun = CalculatorStorage.outputMathTextData.text graphView.clearGraph() } + drawGraph(graphView.getMinX(), graphView.getMaxX()) //drawGraph(graphView.getMinX(), graphView.getMaxX()) } + drawGraph(BigDecimal(-10), BigDecimal(10)) return viewBinding.root } - private fun executeBack() { - viewBinding.root.findNavController().navigateUp() + private fun initBarButtons() { + viewBinding.calculatorButton.setOnClickListener { showCalculatorFragment() } + viewBinding.historyButton.setOnClickListener { showHistoryFragment() } + // viewBinding.cameraButton.setOnClickListener { showCameraFragment() } // TODO: uncomment when camera is implemented{ + viewBinding.settingsButton.setOnClickListener { showSettingsFragment() } + viewBinding.aboutButton.setOnClickListener { showAboutFragment() } } private var drawGraphJob: Job? = null @@ -91,7 +99,7 @@ class GraphFragment : Fragment() { top: BigDecimal ) { graphView.addPoint(top, - BigDecimal(approximator.approximate( + BigDecimal(approximator.approximate( firstSolutionText, varStr, top.toString() @@ -100,4 +108,33 @@ class GraphFragment : Fragment() { } + private fun showCalculatorFragment() { + executeBack() + } + + private fun showHistoryFragment() { + executeBack() + showFragment(R.id.action_calculatorFragment_to_historyFragment) + } + + private fun showCameraFragment() { + executeBack() + showFragment(R.id.action_calculatorFragment_to_cameraFragment) + } + + private fun showAboutFragment() { + showFragment(R.id.action_graphFragment_to_aboutFragment) + } + + private fun showSettingsFragment() { + showFragment(R.id.action_graphFragment_to_settingsFragment) + } + + private fun showFragment(navigationId: Int) { + viewBinding.root.findNavController().navigate(navigationId) + } + + private fun executeBack() { + viewBinding.root.findNavController().navigateUp() + } } diff --git a/app/src/main/java/com/fintamath/fragment/history/HistoryFragment.kt b/app/src/main/java/com/fintamath/fragment/history/HistoryFragment.kt index f934fb59..03cbc930 100644 --- a/app/src/main/java/com/fintamath/fragment/history/HistoryFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/history/HistoryFragment.kt @@ -49,7 +49,8 @@ class HistoryFragment : Fragment() { private fun initBarButtons() { viewBinding.calculatorButton.setOnClickListener { showCalculatorFragment() } -// viewBinding.cameraButton.setOnClickListener { showCameraFragment() } // TODO: uncomment when camera is implemented + viewBinding.graphButton.setOnClickListener { showGraphFragment() } + // viewBinding.cameraButton.setOnClickListener { showCameraFragment() } // TODO: uncomment when camera is implemented{ viewBinding.settingsButton.setOnClickListener { showSettingsFragment() } viewBinding.aboutButton.setOnClickListener { showAboutFragment() } } @@ -78,6 +79,11 @@ class HistoryFragment : Fragment() { executeBack() } + private fun showGraphFragment() { + executeBack() + showFragment(R.id.action_calculatorFragment_to_graphFragment) + } + private fun showCameraFragment() { executeBack() showFragment(R.id.action_calculatorFragment_to_cameraFragment) diff --git a/app/src/main/res/drawable/ic_graph.xml b/app/src/main/res/drawable/ic_graph.xml new file mode 100644 index 00000000..b64ead00 --- /dev/null +++ b/app/src/main/res/drawable/ic_graph.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/fragment_calculator.xml b/app/src/main/res/layout/fragment_calculator.xml index fb312420..a8d9dac1 100644 --- a/app/src/main/res/layout/fragment_calculator.xml +++ b/app/src/main/res/layout/fragment_calculator.xml @@ -43,37 +43,36 @@ + android:background="@drawable/background_bar_button" + android:src="@drawable/ic_graph" + android:contentDescription="@string/graph"/> + android:background="@drawable/background_bar_enabled_button" + android:src="@drawable/ic_calculator" + android:contentDescription="@string/calculator"/> + android:src="@drawable/ic_history" + android:contentDescription="@string/history"/> + android:src="@drawable/ic_logo_small" + android:contentDescription="@string/app_name" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -27,10 +31,6 @@ android:id="@+id/action_calculatorFragment_to_settingsFragment" app:destination="@id/settingsFragment" /> - - + tools:layout="@layout/fragment_graph" > + + + + + + Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 18/26] Remove drawGraph from CalculatorFragment --- .../fragment/calculator/CalculatorFragment.kt | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt index 2b887081..83b43fa1 100644 --- a/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/calculator/CalculatorFragment.kt @@ -268,57 +268,9 @@ class CalculatorFragment : Fragment() { } } } - - drawGraph(firstSolutionText) - } - } - - private fun drawGraph(firstSolutionText: String) { - drawGraphJob?.cancel() - - drawGraphJob = viewLifecycleOwner.lifecycleScope.launch { - if (calculatorProcessor.getVariableCount(firstSolutionText) != 1) { - return@launch - } - - val varStr = calculatorProcessor.getLastVariable(firstSolutionText) - val min = BigDecimal(-10) - val max = BigDecimal(10) - val mid = (min + max) / BigDecimal(2) - - drawGraphPoint(firstSolutionText, varStr, mid) - - val delta = BigDecimal("0.1") - var bottom = mid - delta - var top = mid + delta - - while (bottom >= min) { - yield() - - drawGraphPoint(firstSolutionText, varStr, bottom) - drawGraphPoint(firstSolutionText, varStr, top) - - bottom -= delta - top += delta - } } } - private fun drawGraphPoint( - firstSolutionText: String, - varStr: String, - top: BigDecimal - ) { - Log.d( - "fintamath", - calculatorProcessor.approximate( - firstSolutionText, - varStr, - top.toString() - ) - ) - } - private fun startLoading() { viewBinding.outSolutionView.showLoading() } From 1ee03ba43f6c998bed07c0582df23f03f64d773b Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 19/26] Fixes in GraphFragment --- .../fintamath/fragment/graph/GraphFragment.kt | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index 90c6cba3..fc232f48 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -11,7 +11,6 @@ import com.fintamath.R import com.fintamath.calculator.Approximator import com.fintamath.databinding.FragmentGraphBinding import com.fintamath.storage.CalculatorStorage -import com.fintamath.widget.graph.GraphView import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.yield @@ -20,9 +19,15 @@ import java.math.BigDecimal class GraphFragment : Fragment() { private lateinit var viewBinding: FragmentGraphBinding - private lateinit var graphView: GraphView + private lateinit var approximator: Approximator - private var currentFun = CalculatorStorage.outputMathTextData.text + private var currentMathText = CalculatorStorage.outputMathTextData.text + + private var drawGraphJob: Job? = null + + private val initialMinX = BigDecimal(-10) + private val initialMaxX = BigDecimal(10) + private val graphPointNum = 200 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -31,20 +36,18 @@ class GraphFragment : Fragment() { viewBinding = FragmentGraphBinding.inflate(inflater, container, false) initBarButtons() + initApproximator() - graphView = viewBinding.root.findViewById(R.id.graphView) - - graphView.setOnScrollOrScale { - if (currentFun != CalculatorStorage.outputMathTextData.text) { - currentFun = CalculatorStorage.outputMathTextData.text - graphView.clearGraph() + viewBinding.graphView.setOnScrollOrScale { + if (currentMathText != CalculatorStorage.outputMathTextData.text) { + currentMathText = CalculatorStorage.outputMathTextData.text + viewBinding.graphView.clearGraph() } - drawGraph(graphView.getMinX(), graphView.getMaxX()) - //drawGraph(graphView.getMinX(), graphView.getMaxX()) + drawGraph(viewBinding.graphView.getMinX(), viewBinding.graphView.getMaxX()) } - drawGraph(BigDecimal(-10), BigDecimal(10)) + drawGraph(initialMinX, initialMaxX) return viewBinding.root } @@ -57,8 +60,9 @@ class GraphFragment : Fragment() { viewBinding.aboutButton.setOnClickListener { showAboutFragment() } } - private var drawGraphJob: Job? = null - private val approximator = Approximator() + private fun initApproximator() { + approximator = Approximator() + } private fun drawGraph(minX: BigDecimal, maxX: BigDecimal) { drawGraph(CalculatorStorage.outputMathTextData.text, minX, maxX) @@ -72,20 +76,20 @@ class GraphFragment : Fragment() { return@launch } - val varStr = approximator.getLastVariable(firstSolutionText) - val mid = (min + max) / BigDecimal(2) + val varName = approximator.getLastVariable(firstSolutionText) + val mid = (min + max).divide(BigDecimal(2)) - drawGraphPoint(firstSolutionText, varStr, mid) + drawGraphPoint(firstSolutionText, varName, mid) - val delta = (max - min) / BigDecimal(200) + val delta = (max - min).divide(BigDecimal(graphPointNum)) var bottom = mid - delta var top = mid + delta while (bottom >= min) { yield() - drawGraphPoint(firstSolutionText, varStr, bottom) - drawGraphPoint(firstSolutionText, varStr, top) + drawGraphPoint(firstSolutionText, varName, bottom) + drawGraphPoint(firstSolutionText, varName, top) bottom -= delta top += delta @@ -95,19 +99,14 @@ class GraphFragment : Fragment() { private fun drawGraphPoint( firstSolutionText: String, - varStr: String, - top: BigDecimal + varName: String, + varValue: BigDecimal ) { - graphView.addPoint(top, - BigDecimal(approximator.approximate( - firstSolutionText, - varStr, - top.toString() - ))) - + val approxValueStr = approximator.approximate(firstSolutionText, varName, varValue.toString()) + val approxValue = BigDecimal(approxValueStr.replace("*10^", "e")) + viewBinding.graphView.addPoint(varValue, approxValue) } - private fun showCalculatorFragment() { executeBack() } From 3277895b3a00c0e66158107a5b1175c78adca9c6 Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 20/26] Change pointPaint color and strokeWidth --- app/src/main/java/com/fintamath/widget/graph/GraphView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt index c7357a8d..5f051a84 100644 --- a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -29,8 +29,8 @@ class GraphView( } private val pointPaint = Paint().apply { - color = Color.LTGRAY - strokeWidth = 1f + color = Color.CYAN + strokeWidth = 4f } private var offsetX = 0.0f From 6a8492491cc4dd0e7d30947c097ddb420ad124ad Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 21/26] Fix variable value parsing in approximation --- .../main/java/com/fintamath/fragment/graph/GraphFragment.kt | 5 +++-- lib/src/Fintamath.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index fc232f48..346aa372 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -102,8 +102,9 @@ class GraphFragment : Fragment() { varName: String, varValue: BigDecimal ) { - val approxValueStr = approximator.approximate(firstSolutionText, varName, varValue.toString()) - val approxValue = BigDecimal(approxValueStr.replace("*10^", "e")) + val varValueStr = varValue.toString().replace("E", "*10^") + val approxValueStr = approximator.approximate(firstSolutionText, varName, varValueStr) + val approxValue = BigDecimal(approxValueStr.replace("*10^", "E")) viewBinding.graphView.addPoint(varValue, approxValue) } diff --git a/lib/src/Fintamath.cpp b/lib/src/Fintamath.cpp index ad36ac2a..e9a0200b 100644 --- a/lib/src/Fintamath.cpp +++ b/lib/src/Fintamath.cpp @@ -151,7 +151,7 @@ extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Calculator_approximat updateCachedExpression(exprStr); Expression approxExpr = exprCached; - approxExpr.setVariable(Variable(varStr), Real(valStr)); + approxExpr.setVariable(Variable(varStr), Expression(valStr)); approxExpr = approxExpr.approximate(); if (auto res = cast(approxExpr.toMinimalObject())) { From c368aa4c3c42c561a58d35167d52cc71fd71a58c Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 22/26] Do not draw point when approximation string is empty --- .../fintamath/fragment/graph/GraphFragment.kt | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index 346aa372..6b8f4f5c 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -102,10 +102,25 @@ class GraphFragment : Fragment() { varName: String, varValue: BigDecimal ) { - val varValueStr = varValue.toString().replace("E", "*10^") - val approxValueStr = approximator.approximate(firstSolutionText, varName, varValueStr) - val approxValue = BigDecimal(approxValueStr.replace("*10^", "E")) - viewBinding.graphView.addPoint(varValue, approxValue) + val approxValueStr = approximator.approximate( + firstSolutionText, + varName, + toFintamathReal(varValue) + ) + + if (approxValueStr.isEmpty()) { + return + } + + viewBinding.graphView.addPoint(varValue, toBigDecimal(approxValueStr)) + } + + private fun toFintamathReal(bigDecimal: BigDecimal): String { + return bigDecimal.toString().replace("E", "*10^") + } + + private fun toBigDecimal(fintamathReal: String): BigDecimal { + return BigDecimal(fintamathReal.replace("*10^", "E")) } private fun showCalculatorFragment() { From d9a8e27cf24f4d9465acc4064a3ceaa5b2a2044f Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 23/26] Fix variable value parsing in approximation --- lib/src/Fintamath.cpp | 47 +++++++------------------------------------ 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/lib/src/Fintamath.cpp b/lib/src/Fintamath.cpp index e9a0200b..16a7bfa4 100644 --- a/lib/src/Fintamath.cpp +++ b/lib/src/Fintamath.cpp @@ -143,41 +143,6 @@ extern "C" JNIEXPORT void Java_com_fintamath_calculator_Calculator_setPrecision( } } -extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Calculator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { - std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); - std::string varStr = env->GetStringUTFChars(varJStr, nullptr); - std::string valStr = env->GetStringUTFChars(valJStr, nullptr); - - updateCachedExpression(exprStr); - - Expression approxExpr = exprCached; - approxExpr.setVariable(Variable(varStr), Expression(valStr)); - approxExpr = approxExpr.approximate(); - - if (auto res = cast(approxExpr.toMinimalObject())) { - return env->NewStringUTF(res->toString(5).c_str()); - } - - return env->NewStringUTF(""); -} - -extern "C" JNIEXPORT jint Java_com_fintamath_calculator_Calculator_getVariableCount(JNIEnv *env, jobject instance, jstring exprJStr) { - std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); - updateCachedExpression(exprStr); - return jint(exprVariablesCached.size()); -} - -extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Calculator_getLastVariable(JNIEnv *env, jobject instance, jstring exprJStr) { - std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); - updateCachedExpression(exprStr); - - if (exprVariablesCached.empty()) { - return env->NewStringUTF(""); - } - - return env->NewStringUTF(exprVariablesCached.back().toString().c_str()); -} - extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Approximator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { @@ -187,12 +152,14 @@ Java_com_fintamath_calculator_Approximator_approximate(JNIEnv *env, jobject inst updateCachedExpression(exprStr); - Expression approxExpr = exprCached; - approxExpr.setVariable(Variable(varStr), Real(valStr)); - approxExpr = approxExpr.approximate(); + if (auto val = convert(*Expression(valStr).toMinimalObject())) { + Expression approxExpr = exprCached; + approxExpr.setVariable(Variable(varStr), *val); + approxExpr = approxExpr.approximate(); - if (auto res = cast(approxExpr.toMinimalObject())) { - return env->NewStringUTF(res->toString(5).c_str()); + if (auto res = convert(*approxExpr.toMinimalObject())) { + return env->NewStringUTF(res->toString(5).c_str()); + } } return env->NewStringUTF(""); From fa556babfc80e5193650868fc168bb991b1e231a Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 24/26] Improve performance --- .../fintamath/fragment/graph/GraphFragment.kt | 5 ++++- lib/src/Fintamath.cpp | 17 ++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index 6b8f4f5c..93e2de19 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -11,6 +11,8 @@ import com.fintamath.R import com.fintamath.calculator.Approximator import com.fintamath.databinding.FragmentGraphBinding import com.fintamath.storage.CalculatorStorage +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.yield @@ -23,6 +25,7 @@ class GraphFragment : Fragment() { private var currentMathText = CalculatorStorage.outputMathTextData.text + private val drawGraphScope = CoroutineScope(Job() + Dispatchers.Main) private var drawGraphJob: Job? = null private val initialMinX = BigDecimal(-10) @@ -71,7 +74,7 @@ class GraphFragment : Fragment() { private fun drawGraph(firstSolutionText: String, min: BigDecimal, max: BigDecimal) { drawGraphJob?.cancel() - drawGraphJob = viewLifecycleOwner.lifecycleScope.launch { + drawGraphJob = drawGraphScope.launch { if (approximator.getVariableCount(firstSolutionText) != 1) { return@launch } diff --git a/lib/src/Fintamath.cpp b/lib/src/Fintamath.cpp index 16a7bfa4..b8c8aec8 100644 --- a/lib/src/Fintamath.cpp +++ b/lib/src/Fintamath.cpp @@ -143,8 +143,7 @@ extern "C" JNIEXPORT void Java_com_fintamath_calculator_Calculator_setPrecision( } } -extern "C" -JNIEXPORT jstring +extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Approximator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); std::string varStr = env->GetStringUTFChars(varJStr, nullptr); @@ -152,14 +151,12 @@ Java_com_fintamath_calculator_Approximator_approximate(JNIEnv *env, jobject inst updateCachedExpression(exprStr); - if (auto val = convert(*Expression(valStr).toMinimalObject())) { - Expression approxExpr = exprCached; - approxExpr.setVariable(Variable(varStr), *val); - approxExpr = approxExpr.approximate(); + Expression approxExpr = exprCached; + approxExpr.setVariable(Variable(varStr), Real(valStr)); + approxExpr = approxExpr.approximate(); - if (auto res = convert(*approxExpr.toMinimalObject())) { - return env->NewStringUTF(res->toString(5).c_str()); - } + if (auto res = convert(*approxExpr.toMinimalObject())) { + return env->NewStringUTF(res->toString(5).c_str()); } return env->NewStringUTF(""); @@ -181,5 +178,3 @@ extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Approximator_getLastV return env->NewStringUTF(exprVariablesCached.back().toString().c_str()); } - - From 44fdf48f2a6c7928ca25ee9aabf4337ae504811d Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 13:42:50 +0300 Subject: [PATCH 25/26] Do not recalculate points --- .../main/java/com/fintamath/fragment/graph/GraphFragment.kt | 5 ++++- app/src/main/java/com/fintamath/widget/graph/GraphView.kt | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt index 93e2de19..e5f3c401 100644 --- a/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt +++ b/app/src/main/java/com/fintamath/fragment/graph/GraphFragment.kt @@ -5,7 +5,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import com.fintamath.R import com.fintamath.calculator.Approximator @@ -105,6 +104,10 @@ class GraphFragment : Fragment() { varName: String, varValue: BigDecimal ) { + if (viewBinding.graphView.hasPoint(varValue)) { + return + } + val approxValueStr = approximator.approximate( firstSolutionText, varName, diff --git a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt index 5f051a84..ccfa7fe4 100644 --- a/app/src/main/java/com/fintamath/widget/graph/GraphView.kt +++ b/app/src/main/java/com/fintamath/widget/graph/GraphView.kt @@ -103,6 +103,10 @@ class GraphView( invalidate() } + fun hasPoint(x: BigDecimal): Boolean { + return points.contains(x) + } + private fun onScrollOrScale() { updateLambda() } From 1aff7b88443528d8e0eb6bd4f825e2943630fd5d Mon Sep 17 00:00:00 2001 From: fintarin Date: Wed, 27 Dec 2023 14:25:12 +0300 Subject: [PATCH 26/26] Improve Approximator --- lib/src/Fintamath.cpp | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/lib/src/Fintamath.cpp b/lib/src/Fintamath.cpp index b8c8aec8..a5273caf 100644 --- a/lib/src/Fintamath.cpp +++ b/lib/src/Fintamath.cpp @@ -68,11 +68,22 @@ void stopCurrentCalculations() { } void updateCachedExpression(const std::string &exprStr) { - if (exprStrCached != exprStr) { + if (exprStrCached == exprStr) { + return; + } + + try { exprStrCached = exprStr; exprCached = Expression(exprStrCached); exprVariablesCached = exprCached.getVariables(); } + catch (const std::exception &exc) { + __android_log_print(ANDROID_LOG_DEBUG, loggerTag, "%s", exc.what()); + + exprStrCached = ""; + exprCached = 0; + exprVariablesCached.clear(); + } } extern "C" JNIEXPORT void Java_com_fintamath_calculator_Calculator_calculate(JNIEnv *env, jobject instance, jstring inJStr) { @@ -143,20 +154,32 @@ extern "C" JNIEXPORT void Java_com_fintamath_calculator_Calculator_setPrecision( } } -extern "C" JNIEXPORT jstring -Java_com_fintamath_calculator_Approximator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { +extern "C" JNIEXPORT jstring Java_com_fintamath_calculator_Approximator_approximate(JNIEnv *env, jobject instance, jstring exprJStr, jstring varJStr, jstring valJStr) { std::string exprStr = env->GetStringUTFChars(exprJStr, nullptr); std::string varStr = env->GetStringUTFChars(varJStr, nullptr); std::string valStr = env->GetStringUTFChars(valJStr, nullptr); updateCachedExpression(exprStr); - Expression approxExpr = exprCached; - approxExpr.setVariable(Variable(varStr), Real(valStr)); - approxExpr = approxExpr.approximate(); + try { + constexpr unsigned approxPrecision = 100; + constexpr unsigned outputPrecision = 5; - if (auto res = convert(*approxExpr.toMinimalObject())) { - return env->NewStringUTF(res->toString(5).c_str()); + Real::ScopedSetPrecision setPrecision(approxPrecision); + + Variable var(varStr); + Real val(valStr); + + Expression approxExpr = exprCached; + approxExpr.setVariable(var, val); + approxExpr = approxExpr.approximate(); + + if (auto res = convert(*approxExpr.toMinimalObject())) { + return env->NewStringUTF(res->toString(outputPrecision).c_str()); + } + } + catch (const std::exception &exc) { + __android_log_print(ANDROID_LOG_DEBUG, loggerTag, "%s", exc.what()); } return env->NewStringUTF("");