This commit is contained in:
serso 2011-12-04 17:28:11 +04:00
parent 474707c9a3
commit 5eb0df2e1e
2 changed files with 54 additions and 13 deletions

View File

@ -8,10 +8,8 @@ package org.solovyev.android.calculator;
import android.app.Activity; import android.app.Activity;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.util.Log;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.widget.Toast; import android.widget.Toast;
@ -27,6 +25,8 @@ import jscl.text.ParseException;
import org.achartengine.ChartFactory; import org.achartengine.ChartFactory;
import org.achartengine.GraphicalView; import org.achartengine.GraphicalView;
import org.achartengine.chart.LineChart; import org.achartengine.chart.LineChart;
import org.achartengine.chart.PointStyle;
import org.achartengine.chart.XYChart;
import org.achartengine.model.Point; import org.achartengine.model.Point;
import org.achartengine.model.XYMultipleSeriesDataset; import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries; import org.achartengine.model.XYSeries;
@ -36,6 +36,7 @@ import org.achartengine.renderer.XYSeriesRenderer;
import org.achartengine.tools.PanListener; import org.achartengine.tools.PanListener;
import org.achartengine.tools.ZoomEvent; import org.achartengine.tools.ZoomEvent;
import org.achartengine.tools.ZoomListener; import org.achartengine.tools.ZoomListener;
import org.achartengine.util.MathHelper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.MutableObject; import org.solovyev.common.utils.MutableObject;
@ -61,7 +62,7 @@ public class CalculatorPlotActivity extends Activity {
public static final String INPUT = "org.solovyev.android.calculator.CalculatorPlotActivity_input"; public static final String INPUT = "org.solovyev.android.calculator.CalculatorPlotActivity_input";
public static final long EVAL_DELAY_MILLIS = 300; public static final long EVAL_DELAY_MILLIS = 400;
/** /**
* The encapsulated graphical view. * The encapsulated graphical view.
@ -74,6 +75,8 @@ public class CalculatorPlotActivity extends Activity {
@NotNull @NotNull
private Constant variable; private Constant variable;
private static final double MAX_Y_DIFF = Math.pow(10, 10);
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -112,11 +115,13 @@ public class CalculatorPlotActivity extends Activity {
graphContainer.removeView(graphicalView); graphContainer.removeView(graphicalView);
} }
final LineChart chart = prepareChart(minValue, maxValue, expression, variable); final XYChart chart = prepareChart(minValue, maxValue, expression, variable);
// reverting boundaries (as in prepareChart() we add some cached values ) // reverting boundaries (as in prepareChart() we add some cached values )
chart.getRenderer().setXAxisMin(DEFAULT_MIN_NUMBER); chart.getRenderer().setXAxisMin(DEFAULT_MIN_NUMBER);
chart.getRenderer().setYAxisMin(DEFAULT_MIN_NUMBER);
chart.getRenderer().setXAxisMax(DEFAULT_MAX_NUMBER); chart.getRenderer().setXAxisMax(DEFAULT_MAX_NUMBER);
chart.getRenderer().setYAxisMax(DEFAULT_MAX_NUMBER);
graphicalView = new GraphicalView(this, chart); graphicalView = new GraphicalView(this, chart);
graphicalView.addZoomListener(new ZoomListener() { graphicalView.addZoomListener(new ZoomListener() {
@Override @Override
@ -140,7 +145,7 @@ public class CalculatorPlotActivity extends Activity {
graphContainer.addView(graphicalView); graphContainer.addView(graphicalView);
} }
private void updateDataSets(@NotNull final LineChart chart) { private void updateDataSets(@NotNull final XYChart chart) {
pendingOperation.setObject(new Runnable() { pendingOperation.setObject(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -199,7 +204,7 @@ public class CalculatorPlotActivity extends Activity {
@NotNull @NotNull
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>(); private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
private static LineChart prepareChart(final double minValue, final double maxValue, @NotNull final Generic expression, @NotNull final Constant variable) { private static XYChart prepareChart(final double minValue, final double maxValue, @NotNull final Generic expression, @NotNull final Constant variable) {
final XYSeries realSeries = new XYSeries(getRealFunctionName(expression, variable)); final XYSeries realSeries = new XYSeries(getRealFunctionName(expression, variable));
final XYSeries imagSeries = new XYSeries(getImagFunctionName(expression, variable)); final XYSeries imagSeries = new XYSeries(getImagFunctionName(expression, variable));
@ -212,11 +217,12 @@ public class CalculatorPlotActivity extends Activity {
} }
final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
renderer.addSeriesRenderer(createCommonRenderer());
renderer.setShowGrid(true); renderer.setShowGrid(true);
renderer.setXTitle(variable.getName()); renderer.setXTitle(variable.getName());
renderer.setYTitle("f(" + variable.getName() + ")"); renderer.setYTitle("f(" + variable.getName() + ")");
//renderer.setYAxisAlign(Paint.Align.CENTER, 0); renderer.setChartTitleTextSize(20);
renderer.addSeriesRenderer(createCommonRenderer());
if (imagExists) { if (imagExists) {
renderer.addSeriesRenderer(createImagRenderer()); renderer.addSeriesRenderer(createImagRenderer());
} }
@ -226,19 +232,28 @@ public class CalculatorPlotActivity extends Activity {
private static XYSeriesRenderer createImagRenderer() { private static XYSeriesRenderer createImagRenderer() {
final XYSeriesRenderer imagRenderer = createCommonRenderer(); final XYSeriesRenderer imagRenderer = createCommonRenderer();
imagRenderer.setStroke(BasicStroke.DOTTED); imagRenderer.setStroke(BasicStroke.DASHED);
imagRenderer.setColor(Color.DKGRAY);
return imagRenderer; return imagRenderer;
} }
private static boolean addXY(double minValue, double maxValue, Generic expression, Constant variable, @NotNull XYSeries realSeries, @NotNull XYSeries imagSeries) { private static boolean addXY(double minValue, double maxValue, Generic expression, Constant variable, @NotNull XYSeries realSeries, @NotNull XYSeries imagSeries) {
boolean imagExists = false; boolean imagExists = false;
final double min = 1.5 * Math.min(minValue, maxValue); double min = Math.min(minValue, maxValue);
final double max = 1.5 * Math.max(minValue, maxValue); double max = Math.max(minValue, maxValue);
double dist = max - min;
min = min - dist;
max = max + dist;
final int numberOfSteps = DEFAULT_NUMBER_OF_STEPS; final int numberOfSteps = DEFAULT_NUMBER_OF_STEPS;
final double step = Math.max((max - min) / numberOfSteps, 0.001); final double step = Math.max((max - min) / numberOfSteps, 0.000000001);
double x = min; double x = min;
Double prevRealY = null;
Double prevX = null;
Double prevImagY = null;
double maxY = Double.NEGATIVE_INFINITY;
double minY = Double.POSITIVE_INFINITY;
while (x <= max) { while (x <= max) {
boolean needToCalculateRealY = needToCalculate(realSeries, step, x); boolean needToCalculateRealY = needToCalculate(realSeries, step, x);
@ -248,14 +263,24 @@ public class CalculatorPlotActivity extends Activity {
final Complex c = unwrap(numeric); final Complex c = unwrap(numeric);
Double y = prepareY(c.realPart()); Double y = prepareY(c.realPart());
if (y != null) { if (y != null) {
//addInfPoint(realSeries, prevX, x, prevRealY, y);
realSeries.add(x, y); realSeries.add(x, y);
maxY = Math.max(maxY, y);
minY = Math.min(minY, y);
prevRealY = y;
prevX = x;
} }
boolean needToCalculateImagY = needToCalculate(imagSeries, step, x); boolean needToCalculateImagY = needToCalculate(imagSeries, step, x);
if (needToCalculateImagY) { if (needToCalculateImagY) {
y = prepareY(c.imaginaryPart()); y = prepareY(c.imaginaryPart());
if (y != null) { if (y != null) {
//addInfPoint(imagSeries, prevX, x, prevImagY, y);
imagSeries.add(x, y); imagSeries.add(x, y);
maxY = Math.max(maxY, y);
minY = Math.min(minY, y);
prevImagY = y;
prevX = x;
} }
if (c.imaginaryPart() != 0d) { if (c.imaginaryPart() != 0d) {
imagExists = true; imagExists = true;
@ -268,7 +293,12 @@ public class CalculatorPlotActivity extends Activity {
final Complex c = unwrap(numeric); final Complex c = unwrap(numeric);
Double y = prepareY(c.imaginaryPart()); Double y = prepareY(c.imaginaryPart());
if (y != null) { if (y != null) {
//addInfPoint(imagSeries, prevX, x, prevImagY, y);
imagSeries.add(x, y); imagSeries.add(x, y);
maxY = Math.max(maxY, y);
minY = Math.min(minY, y);
prevImagY = y;
prevX = x;
} }
if (c.imaginaryPart() != 0d) { if (c.imaginaryPart() != 0d) {
imagExists = true; imagExists = true;
@ -287,6 +317,14 @@ public class CalculatorPlotActivity extends Activity {
return imagExists; return imagExists;
} }
private static void addInfPoint(@NotNull XYSeries series, @Nullable Double prevX, @NotNull Double x, @Nullable Double prevY, @NotNull Double y) {
if ( prevX != null && prevY != null) {
if ( Math.abs(prevY / y) > MAX_Y_DIFF || Math.abs(y / prevY) > MAX_Y_DIFF) {
series.add( prevX + Math.abs(x - prevX) / 2, MathHelper.NULL_VALUE);
}
}
}
private static boolean needToCalculate(@NotNull XYSeries series, double step, double x) { private static boolean needToCalculate(@NotNull XYSeries series, double step, double x) {
boolean needToCalculateY = true; boolean needToCalculateY = true;
for ( int i = 0; i < series.getItemCount(); i++ ){ for ( int i = 0; i < series.getItemCount(); i++ ){
@ -319,6 +357,9 @@ public class CalculatorPlotActivity extends Activity {
@NotNull @NotNull
private static XYSeriesRenderer createCommonRenderer() { private static XYSeriesRenderer createCommonRenderer() {
final XYSeriesRenderer renderer = new XYSeriesRenderer(); final XYSeriesRenderer renderer = new XYSeriesRenderer();
renderer.setFillPoints(true);
renderer.setPointStyle(PointStyle.POINT);
renderer.setLineWidth(3);
renderer.setColor(Color.WHITE); renderer.setColor(Color.WHITE);
renderer.setStroke(BasicStroke.SOLID); renderer.setStroke(BasicStroke.SOLID);
return renderer; return renderer;

View File

@ -244,7 +244,7 @@ public class CalculatorEngineTest {
Assert.fail(); Assert.fail();
} catch (ParseException e) { } catch (ParseException e) {
} }
Assert.assertEquals("NaN", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getResult()); Assert.assertEquals("0.34+1.382i", cm.evaluate(JsclOperation.numeric, "ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(ln(100)))))))))))))))").getResult());
try { try {
cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))"); cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos())))))))))))))))))))))))))))))))))))");
Assert.fail(); Assert.fail();