plotting added

This commit is contained in:
serso 2011-12-01 01:52:40 +04:00
parent 048c3a7374
commit 3cc9be18b5
14 changed files with 252 additions and 32 deletions

View File

@ -68,5 +68,10 @@
a:configChanges="orientation|keyboardHidden">
</activity>
<activity a:name=".CalculatorPlotActivity"
a:label="@string/c_plot_graph"
a:configChanges="orientation|keyboardHidden">
</activity>
</application>
</manifest>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2009-2011. Created by serso aka se.solovyev.
~ For more information, please, contact se.solovyev@gmail.com
~ or visit http://se.solovyev.org
-->
<ScrollView xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="fill_parent">
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:orientation="vertical"
a:layout_width="fill_parent"
a:layout_height="fill_parent"
a:scrollbars="vertical"
a:scrollbarAlwaysDrawVerticalTrack="true">
<TextView
xmlns:a="http://schemas.android.com/apk/res/android"
a:id="@+id/error_message_text_view"
a:layout_height="fill_parent"
a:layout_width="wrap_content"
a:padding="6dp"
style="@style/default_text_size"/>
</LinearLayout>
</ScrollView>

View File

@ -276,4 +276,6 @@ Check the \'Round result\' preference in application settings - it should be tur
<string name="msg_5">No parameters are specified for function: {0}</string>
<string name="msg_6">Infinite loop is detected in expression</string>
<string name="c_plot_graph">Graph</string>
</resources>

View File

@ -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();

View File

@ -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.Result> 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;
}
}

View File

@ -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;

View File

@ -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<CalculatorH
if (v instanceof CalculatorDisplay) {
final CalculatorDisplay cd = (CalculatorDisplay)v;
if (cd.isValid()) {
copyResult(activity, cd);
switch (cd.getJsclOperation()) {
case simplify:
Generic genericResult = cd.getGenericResult();
if ( genericResult != null ) {
final Set<Constant> notSystemConstants = new HashSet<Constant>();
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<CalculatorH
}
private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) {
final TextView errorMessageTextView = new TextView(activity);
errorMessageTextView.setText(errorMessage);
final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null);
((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage);
final AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setPositiveButton(R.string.c_cancel, null)
.setView(errorMessageTextView);
.setView(errorMessageView);
builder.create().show();
}
@ -104,9 +150,9 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
copyResult(context, display);
}
private void copyResult(@NotNull Context context, @NotNull final CalculatorDisplay display) {
public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplay display) {
if (display.isValid()) {
final CharSequence text = this.display.getText();
final CharSequence text = display.getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text.toString());
@ -238,12 +284,14 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
display.setText("");
}
display.setJsclOperation(result.getUserOperation());
display.setGenericResult(result.getGenericResult());
} catch (ParseException e) {
handleEvaluationException(expression, display, operation, e);
}
} else {
this.display.setText("");
this.display.setJsclOperation(operation);
this.display.setGenericResult(null);
}
@ -258,6 +306,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e.getMessage());
localDisplay.setText(R.string.c_syntax_error);
localDisplay.setJsclOperation(operation);
localDisplay.setGenericResult(null);
localDisplay.setValid(false);
localDisplay.setErrorMessage(e.getLocalizedMessage());
}
@ -355,6 +404,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
display.setValid(editorHistoryState.isValid());
display.setErrorMessage(editorHistoryState.getErrorMessage());
display.setJsclOperation(editorHistoryState.getJsclOperation());
display.setGenericResult(editorHistoryState.getGenericResult());
}
private void setValuesFromHistory(@NotNull TextView editText, EditorHistoryState editorHistoryState) {

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import org.achartengine.GraphicalActivity;
/**
* User: serso
* Date: 12/1/11
* Time: 12:40 AM
*/
public class CalculatorPlotActivity extends GraphicalActivity{
}

View File

@ -19,6 +19,7 @@ import android.widget.*;
import jscl.text.Identifier;
import jscl.text.MutableInt;
import jscl.text.ParseException;
import jscl.text.Parser;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType;
@ -301,7 +302,7 @@ public class CalculatorVarsActivity extends ListActivity {
if (!StringUtils.isEmpty(name)) {
try {
Identifier.parser.parse(name, new MutableInt(0), null);
Identifier.parser.parse(Parser.Parameters.newInstance(name, new MutableInt(0), CalculatorEngine.instance.getEngine()), null);
result = true;
} catch (ParseException e) {
// not valid name;

View File

@ -6,6 +6,7 @@
package org.solovyev.android.calculator.jscl;
import jscl.math.Generic;
import jscl.text.ParseException;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.model.CalculatorEngine;
@ -21,6 +22,12 @@ public enum JsclOperation {
public String evaluate(@NotNull String expression) throws ParseException {
return CalculatorEngine.instance.getEngine().simplify(expression);
}
@NotNull
@Override
public Generic evaluateGeneric(@NotNull String expression) throws ParseException {
return CalculatorEngine.instance.getEngine().simplifyGeneric(expression);
}
},
elementary(DummyTextProcessor.instance) {
@ -30,6 +37,12 @@ public enum JsclOperation {
return CalculatorEngine.instance.getEngine().elementary(expression);
}
@NotNull
@Override
public Generic evaluateGeneric(@NotNull String expression) throws ParseException {
return CalculatorEngine.instance.getEngine().elementaryGeneric(expression);
}
},
numeric(new FromJsclNumericTextProcessor()) {
@ -38,6 +51,12 @@ public enum JsclOperation {
public String evaluate(@NotNull String expression) throws ParseException {
return CalculatorEngine.instance.getEngine().evaluate(expression);
}
@NotNull
@Override
public Generic evaluateGeneric(@NotNull String expression) throws ParseException {
return CalculatorEngine.instance.getEngine().evaluateGeneric(expression);
}
};
@NotNull
@ -54,4 +73,9 @@ public enum JsclOperation {
@NotNull
public abstract String evaluate(@NotNull String expression) throws ParseException;
@NotNull
public abstract Generic evaluateGeneric(@NotNull String expression) throws ParseException;
}

View File

@ -20,7 +20,7 @@ import java.util.*;
public enum MathType {
numeral_base(50, true, false) {
/* numeral_base(50, true, false) {
private final List<String> tokens = new ArrayList<String>(10);
{
@ -37,7 +37,7 @@ public enum MathType {
public List<String> 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

View File

@ -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<String> calculationResult = new MutableObject<String>(null);
final MutableObject<Generic> calculationResult = new MutableObject<Generic>(null);
final MutableObject<ParseException> exception = new MutableObject<ParseException>(null);
final MutableObject<Thread> calculationThread = new MutableObject<Thread>(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);
}
}

View File

@ -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;
}

View File

@ -36,12 +36,12 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
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<PreparedExpression> {
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);