From 7822663989ae949ecad4ca3d63b08da7e07bc413 Mon Sep 17 00:00:00 2001 From: serso Date: Thu, 1 Dec 2011 01:52:40 +0400 Subject: [PATCH] plotting added --- AndroidManifest.xml | 5 ++ res/layout/display_error_message.xml | 31 ++++++++++ res/values/strings.xml | 2 + .../CalculatorActivityLauncher.java | 55 ++++++++++++++++ .../android/calculator/CalculatorDisplay.java | 12 ++++ .../CalculatorDisplayHistoryState.java | 10 +++ .../android/calculator/CalculatorModel.java | 62 +++++++++++++++++-- .../calculator/CalculatorPlotActivity.java | 17 +++++ .../calculator/CalculatorVarsActivity.java | 3 +- .../calculator/jscl/JsclOperation.java | 24 +++++++ .../android/calculator/math/MathType.java | 6 +- .../calculator/model/CalculatorEngine.java | 25 +++++--- .../calculator/model/NumberBuilder.java | 20 +++--- .../calculator/model/ToJsclTextProcessor.java | 12 ++-- 14 files changed, 252 insertions(+), 32 deletions(-) create mode 100644 res/layout/display_error_message.xml create mode 100644 src/main/java/org/solovyev/android/calculator/CalculatorPlotActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b8d540bf..63493307 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -68,5 +68,10 @@ a:configChanges="orientation|keyboardHidden"> + + + \ No newline at end of file diff --git a/res/layout/display_error_message.xml b/res/layout/display_error_message.xml new file mode 100644 index 00000000..46851234 --- /dev/null +++ b/res/layout/display_error_message.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 4385074d..6ea43e89 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -276,4 +276,6 @@ Check the \'Round result\' preference in application settings - it should be tur No parameters are specified for function: {0} Infinite loop is detected in expression + Graph + diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorActivityLauncher.java b/src/main/java/org/solovyev/android/calculator/CalculatorActivityLauncher.java index 3e3d58c7..4c06922f 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorActivityLauncher.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorActivityLauncher.java @@ -3,6 +3,19 @@ package org.solovyev.android.calculator; import android.content.Context; import android.content.Intent; import android.widget.Toast; +import jscl.math.Expression; +import jscl.math.Generic; +import jscl.math.JsclInteger; +import jscl.math.NumericWrapper; +import jscl.math.function.Constant; +import jscl.math.numeric.Complex; +import jscl.math.numeric.Numeric; +import jscl.math.numeric.Real; +import org.achartengine.ChartFactory; +import org.achartengine.model.XYMultipleSeriesDataset; +import org.achartengine.model.XYSeries; +import org.achartengine.renderer.XYMultipleSeriesRenderer; +import org.achartengine.renderer.XYSeriesRenderer; import org.jetbrains.annotations.NotNull; import org.solovyev.android.calculator.help.HelpActivity; import org.solovyev.common.utils.StringUtils; @@ -42,6 +55,48 @@ public class CalculatorActivityLauncher { context.startActivity(new Intent(context, CalculatorVarsActivity.class)); } + public static void plotGraph(@NotNull final Context context, @NotNull Generic generic, @NotNull Constant constant) throws ArithmeticException { + + final XYSeries series = new XYSeries(generic.toString()); + + final double min = -10; + final double max = 10; + final double step = 0.5; + double x = min; + while (x <= max) { + Generic numeric = generic.substitute(constant, Expression.valueOf(x)).numeric(); + series.add(x, unwrap(numeric)); + x += step; + } + final XYMultipleSeriesDataset data = new XYMultipleSeriesDataset(); + data.addSeries(series); + final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); + renderer.addSeriesRenderer(new XYSeriesRenderer()); + final Intent intent = ChartFactory.getLineChartIntent(context, data, renderer); + intent.setClass(context, CalculatorPlotActivity.class); + context.startActivity(intent); + } + + private static double unwrap(Generic numeric) { + if ( numeric instanceof JsclInteger) { + return ((JsclInteger) numeric).intValue(); + } else if ( numeric instanceof NumericWrapper ) { + return unwrap(((NumericWrapper) numeric).content()); + } else { + throw new ArithmeticException(); + } + } + + private static double unwrap(Numeric content) { + if (content instanceof Real) { + return ((Real) content).doubleValue(); + } else if ( content instanceof Complex) { + return ((Complex) content).realPart(); + } else { + throw new ArithmeticException(); + } + } + public static void createVar(@NotNull final Context context, @NotNull CalculatorModel calculatorModel) { if (calculatorModel.getDisplay().isValid() ) { final String varValue = calculatorModel.getDisplay().getText().toString(); diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java b/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java index 662018cb..130041d9 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorDisplay.java @@ -10,6 +10,7 @@ import android.graphics.Color; import android.text.Html; import android.util.AttributeSet; import android.util.Log; +import jscl.math.Generic; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.jscl.JsclOperation; @@ -35,6 +36,9 @@ public class CalculatorDisplay extends AutoResizeTextView { @NotNull private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, true); + @Nullable + private Generic genericResult; + public CalculatorDisplay(Context context) { super(context); } @@ -106,4 +110,12 @@ public class CalculatorDisplay extends AutoResizeTextView { resizeText(); } + public void setGenericResult(@Nullable Generic genericResult) { + this.genericResult = genericResult; + } + + @Nullable + public Generic getGenericResult() { + return genericResult; + } } diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorDisplayHistoryState.java b/src/main/java/org/solovyev/android/calculator/CalculatorDisplayHistoryState.java index d3fd0b81..bf7b827f 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorDisplayHistoryState.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorDisplayHistoryState.java @@ -5,6 +5,7 @@ package org.solovyev.android.calculator; +import jscl.math.Generic; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.jscl.JsclOperation; @@ -27,6 +28,9 @@ public class CalculatorDisplayHistoryState { @NotNull private JsclOperation jsclOperation; + @Nullable + private Generic genericResult; + private CalculatorDisplayHistoryState() { } @@ -37,6 +41,7 @@ public class CalculatorDisplayHistoryState { result.editorHistoryState = EditorHistoryState.newInstance(display); result.valid = display.isValid(); result.jsclOperation = display.getJsclOperation(); + result.genericResult = display.getGenericResult(); result.errorMessage = display.getErrorMessage(); return result; @@ -61,6 +66,11 @@ public class CalculatorDisplayHistoryState { return errorMessage; } + @Nullable + public Generic getGenericResult() { + return genericResult; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorModel.java b/src/main/java/org/solovyev/android/calculator/CalculatorModel.java index c2d2fb79..823a5af1 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -12,23 +12,31 @@ import android.content.SharedPreferences; import android.os.Handler; import android.text.ClipboardManager; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import jscl.math.Generic; +import jscl.math.function.Constant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.model.ParseException; +import org.solovyev.android.calculator.model.Var; import org.solovyev.android.view.CursorControl; import org.solovyev.android.view.HistoryControl; import org.solovyev.common.BooleanMapper; +import org.solovyev.common.utils.CollectionsUtils; import org.solovyev.common.utils.MutableObject; import org.solovyev.common.utils.StringUtils; import org.solovyev.common.utils.history.HistoryAction; +import java.util.HashSet; +import java.util.Set; + /** * User: serso * Date: 9/12/11 @@ -67,7 +75,42 @@ public enum CalculatorModel implements CursorControl, HistoryControl notSystemConstants = new HashSet(); + for (Constant constant : genericResult.getConstants()) { + Var var = CalculatorEngine.instance.getVarsRegister().get(constant.getName()); + if (var != null && !var.isSystem()) { + notSystemConstants.add(constant); + } + } + + if ( notSystemConstants.size() > 0 ) { + if (notSystemConstants.size() > 1) { + copyResult(activity, cd); + } else { + final Constant constant = CollectionsUtils.getFirstCollectionElement(notSystemConstants); + assert constant != null; + try { + CalculatorActivityLauncher.plotGraph(activity, genericResult, constant); + } catch (ArithmeticException e) { + copyResult(activity, cd); + } + } + } else { + copyResult(activity, cd); + } + } else { + copyResult(activity, cd); + } + break; + case elementary: + case numeric: + copyResult(activity, cd); + break; + } } else { final String errorMessage = cd.getErrorMessage(); if ( errorMessage != null ) { @@ -91,11 +134,14 @@ public enum CalculatorModel implements CursorControl, HistoryControl tokens = new ArrayList(10); { @@ -37,7 +37,7 @@ public enum MathType { public List getTokens() { return tokens; } - }, + },*/ dot(200, true, true, ".") { @Override @@ -138,7 +138,7 @@ public enum MathType { } @Override public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) { - return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit && mathTypeBefore != dot && mathTypeBefore != numeral_base; + return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit && mathTypeBefore != dot /*&& mathTypeBefore != numeral_base*/; } @NotNull diff --git a/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java b/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java index b306065a..df3835da 100644 --- a/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java +++ b/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java @@ -10,13 +10,13 @@ import android.content.SharedPreferences; import jscl.AngleUnit; import jscl.JsclMathEngine; import jscl.MathEngine; +import jscl.math.Generic; import jscl.math.function.Function; import jscl.math.operator.Operator; import jscl.text.ParseInterruptedException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.msg.AndroidMessage; import org.solovyev.common.NumberMapper; import org.solovyev.common.msg.MessageRegistry; import org.solovyev.common.utils.MutableObject; @@ -50,6 +50,7 @@ public enum CalculatorEngine { public static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units"; public static final String ANGLE_UNITS_DEFAULT = "deg"; + public static final int DEFAULT_TIMEOUT = 3000; @NotNull private final Object lock = new Object(); @@ -89,7 +90,7 @@ public enum CalculatorEngine { private ThreadKiller threadKiller = new AndroidThreadKiller(); // calculation thread timeout in milliseconds, after timeout thread would be interrupted - private int timeout = 3000; + private int timeout = DEFAULT_TIMEOUT; @NotNull public String format(@NotNull Double value) { @@ -118,15 +119,20 @@ public enum CalculatorEngine { } public static class Result { + + @NotNull + private Generic genericResult; + @NotNull private String result; @NotNull private JsclOperation userOperation; - public Result(@NotNull String result, @NotNull JsclOperation userOperation) { + public Result(@NotNull String result, @NotNull JsclOperation userOperation, @NotNull Generic genericResult) { this.result = result; this.userOperation = userOperation; + this.genericResult = genericResult; } @NotNull @@ -138,6 +144,11 @@ public enum CalculatorEngine { public JsclOperation getUserOperation() { return userOperation; } + + @NotNull + public Generic getGenericResult() { + return genericResult; + } } public Result evaluate(@NotNull JsclOperation operation, @@ -174,7 +185,7 @@ public enum CalculatorEngine { final JsclOperation finalOperation = operation; final String result; - final MutableObject calculationResult = new MutableObject(null); + final MutableObject calculationResult = new MutableObject(null); final MutableObject exception = new MutableObject(null); final MutableObject calculationThread = new MutableObject(null); @@ -188,7 +199,7 @@ public enum CalculatorEngine { //Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName()); //System.out.println(jsclExpression); calculationThread.setObject(thread); - calculationResult.setObject(finalOperation.evaluate(jsclExpression)); + calculationResult.setObject(finalOperation.evaluateGeneric(jsclExpression)); } catch (ArithmeticException e) { //System.out.println(e.getMessage()); exception.setObject(new ParseException(Messages.msg_1, jsclExpression, e.getMessage())); @@ -239,9 +250,9 @@ public enum CalculatorEngine { throw new ParseException(Messages.msg_4, jsclExpression); } - result = String.valueOf(calculationResult.getObject()).trim(); + final Generic genericResult = calculationResult.getObject(); - return new Result(operation.getFromProcessor().process(result), operation); + return new Result(operation.getFromProcessor().process(genericResult.toString()), operation, genericResult); } } diff --git a/src/main/java/org/solovyev/android/calculator/model/NumberBuilder.java b/src/main/java/org/solovyev/android/calculator/model/NumberBuilder.java index 068902d8..1bf14e34 100644 --- a/src/main/java/org/solovyev/android/calculator/model/NumberBuilder.java +++ b/src/main/java/org/solovyev/android/calculator/model/NumberBuilder.java @@ -6,7 +6,6 @@ package org.solovyev.android.calculator.model; -import jscl.NumeralBase; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.math.MathType; @@ -31,9 +30,9 @@ public class NumberBuilder { private final boolean simpleFormat; - @Nullable + /*@Nullable private NumeralBase nb; - +*/ public NumberBuilder(boolean simpleFormat) { this.simpleFormat = simpleFormat; } @@ -43,17 +42,17 @@ public class NumberBuilder { number = null; final MathType.Result possibleResult; - if ((CollectionsUtils.contains(mathTypeResult.getMathType(), MathType.digit, MathType.numeral_base, MathType.dot, MathType.grouping_separator, MathType.power_10) || + if ((CollectionsUtils.contains(mathTypeResult.getMathType(), MathType.digit, /*MathType.numeral_base,*/ MathType.dot, MathType.grouping_separator, MathType.power_10) || isSignAfterE(mathTypeResult)) && numeralBaseCheck(mathTypeResult)) { if (numberBuilder == null) { numberBuilder = new StringBuilder(); } - if (mathTypeResult.getMathType() != MathType.numeral_base) { + /*if (mathTypeResult.getMathType() != MathType.numeral_base) {*/ numberBuilder.append(mathTypeResult.getMatch()); - } else { + /*} else { nb = NumeralBase.getByPrefix(mathTypeResult.getMatch()); - } + }*/ possibleResult = null; } else { @@ -64,7 +63,7 @@ public class NumberBuilder { } private boolean numeralBaseCheck( @NotNull MathType.Result mathType ) { - if ( mathType.getMathType() == MathType.digit ) { + /*if ( mathType.getMathType() == MathType.digit ) { final Character ch = mathType.getMatch().charAt(0); if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) && !NumeralBase.dec.getAcceptableCharacters().contains(ch) ) { if ( nb == NumeralBase.hex ) { @@ -77,7 +76,8 @@ public class NumberBuilder { } } else { return true; - } + }*/ + return true; } private boolean isSignAfterE(@NotNull MathType.Result mathTypeResult) { @@ -113,7 +113,7 @@ public class NumberBuilder { } numberBuilder = null; - nb = null; + /*nb = null;*/ } else { number = null; } diff --git a/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java b/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java index f9023e2a..5ad51609 100644 --- a/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java +++ b/src/main/java/org/solovyev/android/calculator/model/ToJsclTextProcessor.java @@ -36,12 +36,12 @@ class ToJsclTextProcessor implements TextProcessor { final StringBuilder result = new StringBuilder(); MathType.Result mathTypeResult = null; - MathType mathTypeBefore; + MathType.Result mathTypeBefore = null; for (int i = 0; i < s.length(); i++) { startsWithFinder.setI(i); - mathTypeBefore = mathTypeResult == null ? null : mathTypeResult.getMathType(); + mathTypeBefore = mathTypeResult == null ? null : mathTypeResult; mathTypeResult = MathType.getType(s, i); @@ -49,13 +49,15 @@ class ToJsclTextProcessor implements TextProcessor { final MathType current = mathTypeResult.getMathType(); - if (current.isNeedMultiplicationSignBefore(mathTypeBefore)) { + if (current.isNeedMultiplicationSignBefore(mathTypeBefore.getMathType())) { result.append("*"); } } - if ((mathTypeBefore == MathType.function || mathTypeBefore == MathType.operator) && CollectionsUtils.find(MathType.openGroupSymbols, startsWithFinder) != null) { - throw new ParseException(Messages.msg_5, i, s, mathTypeResult.getMatch()); + if (mathTypeBefore != null && + (mathTypeBefore.getMathType() == MathType.function || mathTypeBefore.getMathType() == MathType.operator) && + CollectionsUtils.find(MathType.openGroupSymbols, startsWithFinder) != null) { + throw new ParseException(Messages.msg_5, i, s, mathTypeBefore.getMatch()); } i = mathTypeResult.processToJscl(result, i);