new plotter

This commit is contained in:
Sergey Solovyev 2013-01-18 20:56:58 +04:00
parent 93c5e2a093
commit b60b576433
13 changed files with 240 additions and 659 deletions

View File

@ -105,20 +105,6 @@
<artifactId>simple-xml</artifactId>
</dependency>
<dependency>
<groupId>achartengine</groupId>
<artifactId>achartengine</artifactId>
<version>0.7.1</version>
</dependency>
<dependency>
<groupId>arity</groupId>
<artifactId>arity</artifactId>
<version>2.1.6</version>
<scope>system</scope>
<systemPath>${project.basedir}/misc/lib/arity-2.1.6.jar</systemPath>
</dependency>
<dependency>
<groupId>admob</groupId>
<artifactId>admob</artifactId>

View File

@ -12,7 +12,6 @@ import android.view.View;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.AndroidUtils2;
@ -400,13 +399,6 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
this.yMax = yMax;
}
public PlotBoundaries(@NotNull XYMultipleSeriesRenderer renderer) {
this.xMin = renderer.getXAxisMin();
this.yMin = renderer.getYAxisMin();
this.xMax = renderer.getXAxisMax();
this.yMax = renderer.getYAxisMax();
}
@NotNull
public static PlotBoundaries newInstance(double xMin, double xMax, double yMin, double yMax) {
return new PlotBoundaries(xMin, xMax, yMin, yMax);

View File

@ -1,5 +1,3 @@
// Copyright (C) 2009-2010 Mihai Preda
package org.solovyev.android.calculator.plot;
@ -10,7 +8,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;
import android.widget.ZoomButtonsController;
import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull;
import java.text.DecimalFormat;
@ -151,19 +148,19 @@ public class CalculatorGraph2dView extends View implements GraphView {
@Override
public void init(@NotNull FunctionViewDef functionViewDef) {
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<ArityPlotFunction>emptyList());
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<PlotFunction>emptyList());
}
public void setFunctionPlotDefs(@NotNull List<ArityPlotFunction> functionPlotDefs) {
public void setPlotFunctions(@NotNull List<PlotFunction> plotFunctions) {
for (ArityPlotFunction functionPlotDef : functionPlotDefs) {
final int arity = functionPlotDef.getFunction().arity();
for (PlotFunction plotFunction : plotFunctions) {
final int arity = plotFunction.getXyFunction().getArity();
if (arity != 0 && arity != 1) {
throw new IllegalArgumentException("Function must have arity 0 or 1 for 2d plot!");
}
}
this.graphViewHelper = this.graphViewHelper.copy(functionPlotDefs);
this.graphViewHelper = this.graphViewHelper.copy(plotFunctions);
clearAllGraphs();
invalidate();
}
@ -200,10 +197,6 @@ public class CalculatorGraph2dView extends View implements GraphView {
drawGraph(canvas);
}
private float eval(Function f, float x) {
return (float) f.eval(x);
}
// distance from (x,y) to the line (x1,y1) to (x2,y2), squared, multiplied by 4
/*
private float distance(float x1, float y1, float x2, float y2, float x, float y) {
@ -224,14 +217,14 @@ public class CalculatorGraph2dView extends View implements GraphView {
return up * up / (dx * dx + dy * dy);
}
private void computeGraph(@NotNull Function function,
private void computeGraph(@NotNull XyFunction f,
float xMin,
float xMax,
float yMin,
float yMax,
@NotNull GraphData graph) {
if (function.arity() == 0) {
final float v = (float) function.eval();
if (f.getArity() == 0) {
final float v = (float) f.eval();
graph.clear();
graph.push(xMin, v);
graph.push(xMax, v);
@ -249,7 +242,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
}
}
if (graph.empty()) {
graph.push(xMin, eval(function, xMin));
graph.push(xMin, (float)f.eval(xMin));
}
final float ratio = getRatio();
@ -270,7 +263,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
}
if (next.empty()) {
float x = leftX + maxStep;
next.push(x, eval(function, x));
next.push(x, (float) f.eval(x));
++nEval;
}
rightX = next.topX();
@ -283,7 +276,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
float dx = rightX - leftX;
float middleX = (leftX + rightX) / 2;
float middleY = eval(function, middleX);
float middleY = (float) f.eval(middleX);
++nEval;
boolean middleIsOutside = (middleY < leftY && middleY < rightY) || (leftY < middleY && rightY < middleY);
if (dx < minStep) {
@ -482,20 +475,20 @@ public class CalculatorGraph2dView extends View implements GraphView {
{
//GRAPH
final List<ArityPlotFunction> functionPlotDefs = graphViewHelper.getFunctionPlotDefs();
final List<PlotFunction> functionPlotDefs = graphViewHelper.getFunctionPlotDefs();
// create path once
final Path path = new Path();
for (int i = 0; i < functionPlotDefs.size(); i++) {
final ArityPlotFunction fpd = functionPlotDefs.get(i);
computeGraph(fpd.getFunction(), xMin, xMax, lastYMin, lastYMax, graphs.get(i));
final PlotFunction fpd = functionPlotDefs.get(i);
computeGraph(fpd.getXyFunction(), xMin, xMax, lastYMin, lastYMax, graphs.get(i));
graphToPath(graphs.get(i), path);
path.transform(matrix);
AbstractCalculatorPlotFragment.applyToPaint(fpd.getLineDef(), paint);
AbstractCalculatorPlotFragment.applyToPaint(fpd.getPlotLineDef(), paint);
canvas.drawPath(path, paint);
}

View File

@ -4,16 +4,10 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import jscl.math.Generic;
import jscl.math.function.Constant;
import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/30/12
@ -44,28 +38,6 @@ public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
graphContainer.removeView((View) graphView);
}
final List<ArityPlotFunction> arityFunctions = new ArrayList<ArityPlotFunction>();
for (PlotFunction plotFunction : plotData.getFunctions()) {
final XyFunction xyFunction = plotFunction.getXyFunction();
final Generic expression = xyFunction.getExpression();
final Constant xVariable = xyFunction.getXVariable();
final Constant yVariable = xyFunction.getYVariable();
final int arity = xyFunction.getArity();
final Function arityFunction;
if (xyFunction.isImag()) {
arityFunction = new ImaginaryArityFunction(arity, expression, xVariable, yVariable);
} else {
arityFunction = new RealArityFunction(arity, expression, xVariable, yVariable);
}
arityFunctions.add(ArityPlotFunction.newInstance(arityFunction, plotFunction.getPlotLineDef()));
}
if ( plotData.isPlot3d() ) {
graphView = new Graph3dView(getActivity());
} else {
@ -74,7 +46,7 @@ public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor()));
//graphView.setXRange((float)plotBoundaries.getXMin(), (float)plotBoundaries.getXMax());
graphView.setFunctionPlotDefs(arityFunctions);
graphView.setPlotFunctions(plotData.getFunctions());
graphContainer.addView((View) graphView);
}
@ -135,96 +107,4 @@ public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
**********************************************************************
*/
private static abstract class AbstractArityFunction extends Function {
protected final int arity;
@NotNull
protected final Generic expression;
@Nullable
protected final Constant xVariable;
@Nullable
protected final Constant yVariable;
@Nullable
private Double constant = null;
public AbstractArityFunction(int arity,
@NotNull Generic expression,
@Nullable Constant xVariable,
@Nullable Constant yVariable) {
this.arity = arity;
this.expression = expression;
this.xVariable = xVariable;
this.yVariable = yVariable;
}
@Override
public final double eval() {
if (constant == null) {
constant = eval0();
}
return constant;
}
protected abstract double eval0();
@Override
public final int arity() {
return arity;
}
}
private static class RealArityFunction extends AbstractArityFunction {
private RealArityFunction(int arity,
@NotNull Generic expression,
@Nullable Constant xVariable,
@Nullable Constant yVariable) {
super(arity, expression, xVariable, yVariable);
}
@Override
public double eval0() {
return PlotUtils.calculatorExpression(expression).realPart();
}
@Override
public double eval(double x) {
return PlotUtils.calculatorExpression(expression, xVariable, x).realPart();
}
@Override
public double eval(double x, double y) {
return PlotUtils.calculatorExpression(expression, xVariable, x, yVariable, y).realPart();
}
}
private static class ImaginaryArityFunction extends AbstractArityFunction {
private ImaginaryArityFunction(int arity,
@NotNull Generic expression,
@Nullable Constant xVariable,
@Nullable Constant yVariable) {
super(arity, expression, xVariable, yVariable);
}
@Override
public double eval0() {
return PlotUtils.calculatorExpression(expression).imaginaryPart();
}
@Override
public double eval(double x) {
return PlotUtils.calculatorExpression(expression, xVariable, x).imaginaryPart();
}
@Override
public double eval(double x, double y) {
return PlotUtils.calculatorExpression(expression, xVariable, x, yVariable, y).imaginaryPart();
}
}
}

View File

@ -3,7 +3,6 @@
package org.solovyev.android.calculator.plot;
import android.graphics.Color;
import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull;
import javax.microedition.khronos.opengles.GL10;
@ -92,9 +91,9 @@ class Graph3d {
return bb;
}
public void update(@NotNull GL11 gl, @NotNull ArityPlotFunction fpd, float zoom) {
final Function function = fpd.getFunction();
final PlotLineDef lineDef = fpd.getLineDef();
public void update(@NotNull GL11 gl, @NotNull PlotFunction fpd, float zoom) {
final XyFunction function = fpd.getXyFunction();
final PlotLineDef lineDef = fpd.getPlotLineDef();
final int NTICK = useHighQuality3d ? 5 : 0;
final float size = 4 * zoom;
@ -230,8 +229,8 @@ class Graph3d {
}
}
private float fillFunctionPolygonVertices(Function function, float size, float[] vertices) {
final int arity = function.arity();
private float fillFunctionPolygonVertices(XyFunction function, float size, float[] vertices) {
final int arity = function.getArity();
final float minX = -size;
final float maxX = size;

View File

@ -174,18 +174,19 @@ public class Graph3dView extends GLView implements GraphView {
@Override
public void init(@NotNull FunctionViewDef functionViewDef) {
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<ArityPlotFunction>emptyList());
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<PlotFunction>emptyList());
}
public void setFunctionPlotDefs(@NotNull List<ArityPlotFunction> functionPlotDefs) {
for (ArityPlotFunction functionPlotDef: functionPlotDefs) {
final int arity = functionPlotDef.getFunction().arity();
@Override
public void setPlotFunctions(@NotNull List<PlotFunction> plotFunctions) {
for (PlotFunction plotFunction: plotFunctions) {
final int arity = plotFunction.getXyFunction().getArity();
if (arity != 0 && arity != 1 && arity != 2) {
throw new IllegalArgumentException("Function must have arity 0 or 1 or 2 for 3d plot!");
}
}
this.graphViewHelper = this.graphViewHelper.copy(functionPlotDefs);
this.graphViewHelper = this.graphViewHelper.copy(plotFunctions);
zoomLevel = 1;
isDirty = true;
}

View File

@ -12,7 +12,7 @@ public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHa
public void init(@NotNull FunctionViewDef functionViewDef);
public void setFunctionPlotDefs(@NotNull List<ArityPlotFunction> functionPlotDefs);
public void setPlotFunctions(@NotNull List<PlotFunction> plotFunctions);
public void onPause();
public void onResume();

View File

@ -16,7 +16,7 @@ public class GraphViewHelper {
private FunctionViewDef functionViewDef = FunctionViewDef.newDefaultInstance();
@NotNull
private List<ArityPlotFunction> functionPlotDefs = Collections.emptyList();
private List<PlotFunction> functionPlotDefs = Collections.emptyList();
private GraphViewHelper() {
}
@ -28,27 +28,27 @@ public class GraphViewHelper {
@NotNull
public static GraphViewHelper newInstance(@NotNull FunctionViewDef functionViewDef,
@NotNull List<ArityPlotFunction> functionPlotDefs) {
@NotNull List<PlotFunction> plotFunctions) {
final GraphViewHelper result = new GraphViewHelper();
result.functionViewDef = functionViewDef;
result.functionPlotDefs = Collections.unmodifiableList(functionPlotDefs);
result.functionPlotDefs = Collections.unmodifiableList(plotFunctions);
return result;
}
@NotNull
public GraphViewHelper copy(@NotNull List<ArityPlotFunction> newFunctionPlotDefs) {
public GraphViewHelper copy(@NotNull List<PlotFunction> plotFunctions) {
final GraphViewHelper result = new GraphViewHelper();
result.functionViewDef = functionViewDef;
result.functionPlotDefs = Collections.unmodifiableList(newFunctionPlotDefs);
result.functionPlotDefs = Collections.unmodifiableList(plotFunctions);
return result;
}
@NotNull
public List<ArityPlotFunction> getFunctionPlotDefs() {
public List<PlotFunction> getFunctionPlotDefs() {
return functionPlotDefs;
}

View File

@ -1,472 +0,0 @@
/*
* 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.plot;
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.chart.CubicLineChart;
import org.achartengine.chart.PointStyle;
import org.achartengine.chart.ScatterChart;
import org.achartengine.chart.XYChart;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.renderer.BasicStroke;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;
import org.achartengine.util.MathHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils;
import java.util.Arrays;
/**
* User: serso
* Date: 12/5/11
* Time: 8:58 PM
*/
public final class PlotUtils {
private static final double MAX_Y_DIFF = 1;
private static final double MAX_X_DIFF = 1;
static final int DEFAULT_NUMBER_OF_STEPS = 100;
// not intended for instantiation
private PlotUtils() {
throw new AssertionError();
}
public static boolean addXY(double minValue,
double maxValue,
@NotNull Generic expression,
@Nullable Constant variable,
@NotNull MyXYSeries realSeries,
@Nullable MyXYSeries imagSeries,
boolean addExtra,
int numberOfSteps) {
boolean imagExists = false;
double min = Math.min(minValue, maxValue);
double max = Math.max(minValue, maxValue);
double dist = max - min;
if (addExtra) {
min = min - dist;
max = max + dist;
}
final double eps = 0.000000001;
final double defaultStep = Math.max(dist / numberOfSteps, eps);
double step = defaultStep;
final Point real = new Point();
final Point imag = new Point();
double x = min;
while (x <= max) {
boolean needToCalculateRealY = realSeries.needToAdd(step, x);
if (needToCalculateRealY) {
final Complex c = variable == null ? calculatorExpression(expression) : calculatorExpression(expression, variable, x);
Double y = prepareY(c.realPart());
if (y != null) {
real.moveToNextPoint(x, y);
addSingularityPoint(realSeries, real);
realSeries.add(x, y);
}
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(step, x);
if (needToCalculateImagY) {
y = prepareY(c.imaginaryPart());
if (y != null) {
imag.moveToNextPoint(x, y);
addSingularityPoint(imagSeries, imag);
imagSeries.add(x, y);
}
if (c.imaginaryPart() != 0d) {
imagExists = true;
}
}
} else {
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(step, x);
if (needToCalculateImagY) {
final Complex c = variable == null ? calculatorExpression(expression) : calculatorExpression(expression, variable, x);
Double y = prepareY(c.imaginaryPart());
if (y != null) {
imag.moveToNextPoint(x, y);
addSingularityPoint(imagSeries, imag);
imagSeries.add(x, y);
}
if (c.imaginaryPart() != 0d) {
imagExists = true;
}
}
}
step = updateStep(real, step, defaultStep / 2);
x += step;
}
return imagExists;
}
@NotNull
static String getImagFunctionName(@Nullable Constant variable) {
if (variable != null) {
return "g(" + variable.getName() + ")" + " = " + "Im(ƒ(" + variable.getName() + "))";
} else {
return "g = Im(ƒ)";
}
}
@NotNull
private static String getRealFunctionName(@NotNull Generic expression, @Nullable Constant variable) {
if (variable != null) {
return "ƒ(" + variable.getName() + ")" + " = " + expression.toString();
} else {
return "ƒ = " + expression.toString();
}
}
@NotNull
static XYChart prepareChart(final double minValue,
final double maxValue,
@NotNull final Generic expression,
@Nullable final Constant variable,
int bgColor,
boolean interpolate,
int realLineColor,
int imagLineColor) {
final MyXYSeries realSeries = new MyXYSeries(getRealFunctionName(expression, variable), DEFAULT_NUMBER_OF_STEPS * 2);
final MyXYSeries imagSeries = new MyXYSeries(getImagFunctionName(variable), DEFAULT_NUMBER_OF_STEPS * 2);
boolean imagExists = addXY(minValue, maxValue, expression, variable, realSeries, imagSeries, false, DEFAULT_NUMBER_OF_STEPS);
final XYMultipleSeriesDataset data = new XYMultipleSeriesDataset();
data.addSeries(realSeries);
if (imagExists) {
data.addSeries(imagSeries);
}
final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
renderer.setShowGrid(true);
renderer.setXTitle(variable != null ? variable.getName() : null);
if (variable != null) {
renderer.setYTitle("f(" + variable.getName() + ")");
} else {
renderer.setYTitle("f");
}
renderer.setChartTitleTextSize(25);
renderer.setAxisTitleTextSize(25);
renderer.setLabelsTextSize(25);
renderer.setLegendTextSize(25);
renderer.setMargins(new int[]{25, 25, 25, 25});
renderer.setApplyBackgroundColor(true);
renderer.setBackgroundColor(bgColor);
renderer.setMarginsColor(bgColor);
renderer.setZoomEnabled(true);
renderer.setZoomButtonsVisible(true);
renderer.addSeriesRenderer(createCommonRenderer(realLineColor));
if (imagExists) {
renderer.addSeriesRenderer(createImagRenderer(imagLineColor));
}
if (interpolate) {
return new CubicLineChart(data, renderer, 0.1f);
} else {
return new ScatterChart(data, renderer);
}
}
static XYSeriesRenderer createImagRenderer(int color) {
final XYSeriesRenderer imagRenderer = createCommonRenderer(color);
imagRenderer.setStroke(BasicStroke.DASHED);
return imagRenderer;
}
@NotNull
private static XYSeriesRenderer createCommonRenderer(int color) {
final XYSeriesRenderer renderer = new XYSeriesRenderer();
renderer.setFillPoints(true);
renderer.setPointStyle(PointStyle.CIRCLE);
renderer.setLineWidth(3);
renderer.setColor(color);
renderer.setStroke(BasicStroke.SOLID);
return renderer;
}
static void handleArithmeticException(@NotNull ArithmeticException e, @NotNull AbstractCalculatorPlotFragment calculatorPlotFragment) {
String message = e.getLocalizedMessage();
if (StringUtils.isEmpty(message)) {
message = e.getMessage();
}
Locator.getInstance().getNotifier().showMessage(R.string.arithmetic_error_while_plot, MessageType.error, Arrays.asList(message));
calculatorPlotFragment.onError();
}
private static class Point {
private static final double DEFAULT = Double.MIN_VALUE;
private double x0 = DEFAULT;
private double x1 = DEFAULT;
private double x2 = DEFAULT;
private double y0 = DEFAULT;
private double y1 = DEFAULT;
private double y2 = DEFAULT;
private Point() {
}
public void moveToNextPoint(double x, double y) {
if ( this.x2 == x ) {
return;
}
this.x0 = this.x1;
this.x1 = this.x2;
this.x2 = x;
this.y0 = this.y1;
this.y1 = this.y2;
this.y2 = y;
}
public boolean isFullyDefined() {
return x0 != DEFAULT && x1 != DEFAULT && x2 != DEFAULT && y0 != DEFAULT && y1 != DEFAULT && y2 != DEFAULT;
}
public double getDx2() {
return x2 - x1;
}
public double getAbsDx2() {
if ( x2 > x1 ) {
return Math.abs(x2 - x1);
} else {
return Math.abs(x1 - x2);
}
}
public double getAbsDx1() {
if ( x1 > x0 ) {
return Math.abs(x1 - x0);
} else {
return Math.abs(x0 - x1);
}
}
public double getAbsDy1() {
if ( y1 > y0 ) {
return Math.abs(y1 - y0);
} else {
return Math.abs(y0 - y1);
}
}
public double getAbsDy2() {
if ( y2 > y1 ) {
return Math.abs(y2 - y1);
} else {
return Math.abs(y1 - y2);
}
}
public double getX0() {
return x0;
}
public double getX1() {
return x1;
}
public double getX2() {
return x2;
}
public boolean isX2Defined() {
return x2 != DEFAULT;
}
public double getY0() {
return y0;
}
public double getY1() {
return y1;
}
public double getY2() {
return y2;
}
public void clearHistory () {
this.x0 = DEFAULT;
this.x1 = DEFAULT;
this.y0 = DEFAULT;
this.y1 = DEFAULT;
}
public double getAbsDyDx2() {
double dx2 = this.getAbsDx2();
double dy2 = this.getAbsDy2();
return dy2 / dx2;
}
public double getAbsDyDx1() {
double dx1 = this.getAbsDx1();
double dy1 = this.getAbsDy1();
return dy1 / dx1;
}
public double getDyDx1() {
double result = getAbsDyDx1();
return y1 > y0 ? result : -result;
}
public double getDyDx2() {
double result = getAbsDyDx2();
return y2 > y1 ? result : -result;
}
@Override
public String toString() {
return "Point{" +
"x0=" + x0 +
", x1=" + x1 +
", x2=" + x2 +
", y0=" + y0 +
", y1=" + y1 +
", y2=" + y2 +
'}';
}
}
private static double updateStep(@NotNull Point real,
double step,
double eps) {
if ( !real.isFullyDefined() ) {
return step;
} else {
double dydx2 = real.getAbsDyDx2();
double dydx1 = real.getAbsDyDx1();
double k = dydx2 / dydx1;
if ( k > 1 ) {
step = step / k;
} else if ( k > 0 ) {
step = step * k;
}
return Math.max(step, eps);
}
}
@NotNull
public static Complex calculatorExpression(@NotNull Generic expression) {
try {
return unwrap(expression.numeric());
} catch (RuntimeException e) {
return NaN;
}
}
@NotNull
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant xVar, double x) {
try {
return unwrap(expression.substitute(xVar, Expression.valueOf(x)).numeric());
} catch (RuntimeException e) {
return NaN;
}
}
@NotNull
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant xVar, double x, @NotNull Constant yVar, double y) {
try {
Generic tmp = expression.substitute(xVar, Expression.valueOf(x));
tmp = tmp.substitute(yVar, Expression.valueOf(y));
return unwrap(tmp.numeric());
} catch (RuntimeException e) {
return NaN;
}
}
public static void addSingularityPoint(@NotNull MyXYSeries series,
@NotNull Point point) {
if (point.isFullyDefined()) {
// y or prevY should be more than 1d because if they are too small false singularity may occur (e.g., 1/0.000000000000000001)
// double dy0 = y1 - y0;
// double dx0 = x1 - x0;
// double dydx0 = dy0 / dx0;
double dy2 = point.getAbsDy2();
double dx2 = point.getAbsDx2();
//double dx1 = x2 - x1;
// double dydx1 = dy2 / dx1;
if ( dy2 > MAX_Y_DIFF && dx2 < MAX_X_DIFF && isDifferentSign(point.getY2(), point.getY1()) && isDifferentSign(point.getDyDx1(), point.getDyDx2())) {
//Log.d(CalculatorPlotActivity.class.getName(), "Singularity: " + point);
//Log.d(CalculatorPlotActivity.class.getName(), String.valueOf(prevX + Math.abs(x - prevX) / 2) + ", null");
series.add(point.getX1() + point.getAbsDx2() / 2, MathHelper.NULL_VALUE);
point.clearHistory();
}
}
}
private static boolean isDifferentSign(@NotNull Double y0, @NotNull Double y1) {
return (y0 >= 0 && y1 < 0) || (y1 >= 0 && y0 < 0);
}
@Nullable
public static Double prepareY(double y) {
if (Double.isNaN(y)) {
return null;
} else {
return y;
}
}
private static final Complex NaN = Complex.valueOf(Double.NaN, 0d);
@NotNull
public static Complex unwrap(@Nullable Generic numeric) {
if (numeric instanceof JsclInteger) {
return Complex.valueOf(((JsclInteger) numeric).intValue(), 0d);
} else if (numeric instanceof NumericWrapper) {
return unwrap(((NumericWrapper) numeric).content());
} else {
return NaN;
}
}
@NotNull
public static Complex unwrap(@Nullable Numeric content) {
if (content instanceof Real) {
return Complex.valueOf(((Real) content).doubleValue(), 0d);
} else if (content instanceof Complex) {
return ((Complex) content);
} else {
throw new ArithmeticException();
}
}
}

View File

@ -0,0 +1,13 @@
package org.solovyev.android.calculator.plot;
/**
* User: serso
* Date: 1/18/13
* Time: 7:44 PM
*/
interface FunctionEvaluator {
int getArity();
double eval();
double eval(double x);
double eval(double x, double y);
}

View File

@ -1,5 +1,7 @@
package org.solovyev.android.calculator.plot;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
@ -9,15 +11,17 @@ import java.util.List;
*/
public class PlotData {
@NotNull
private List<PlotFunction> functions;
private boolean plot3d;
public PlotData(List<PlotFunction> functions, boolean plot3d) {
public PlotData(@NotNull List<PlotFunction> functions, boolean plot3d) {
this.functions = functions;
this.plot3d = plot3d;
}
@NotNull
public List<PlotFunction> getFunctions() {
return functions;
}

View File

@ -0,0 +1,84 @@
/*
* 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.plot;
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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/5/11
* Time: 8:58 PM
*/
public final class PlotUtils {
// not intended for instantiation
private PlotUtils() {
throw new AssertionError();
}
@NotNull
public static Complex calculatorExpression(@NotNull Generic expression) {
try {
return unwrap(expression.numeric());
} catch (RuntimeException e) {
return NaN;
}
}
@NotNull
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant xVar, double x) {
try {
return unwrap(expression.substitute(xVar, Expression.valueOf(x)).numeric());
} catch (RuntimeException e) {
return NaN;
}
}
@NotNull
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant xVar, double x, @NotNull Constant yVar, double y) {
try {
Generic tmp = expression.substitute(xVar, Expression.valueOf(x));
tmp = tmp.substitute(yVar, Expression.valueOf(y));
return unwrap(tmp.numeric());
} catch (RuntimeException e) {
return NaN;
}
}
private static final Complex NaN = Complex.valueOf(Double.NaN, 0d);
@NotNull
public static Complex unwrap(@Nullable Generic numeric) {
if (numeric instanceof JsclInteger) {
return Complex.valueOf(((JsclInteger) numeric).intValue(), 0d);
} else if (numeric instanceof NumericWrapper) {
return unwrap(((NumericWrapper) numeric).content());
} else {
return NaN;
}
}
@NotNull
public static Complex unwrap(@Nullable Numeric content) {
if (content instanceof Real) {
return Complex.valueOf(((Real) content).doubleValue(), 0d);
} else if (content instanceof Complex) {
return ((Complex) content);
} else {
throw new ArithmeticException();
}
}
}

View File

@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.text.StringUtils;
public class XyFunction {
public class XyFunction implements FunctionEvaluator {
/*
**********************************************************************
@ -41,6 +41,9 @@ public class XyFunction {
private int arity;
@NotNull
private final FunctionEvaluator evaluator;
public XyFunction(@NotNull Generic expression,
@Nullable Constant xVariable,
@Nullable Constant yVariable,
@ -52,8 +55,10 @@ public class XyFunction {
if (imag) {
this.expressionString = "Im(" + expression.toString() + ")";
this.evaluator = new ImaginaryEvaluator(this);
} else {
this.expressionString = expression.toString();
this.evaluator = new RealEvaluator(this);
}
this.xVariableName = xVariable == null ? null : xVariable.getName();
this.yVariableName = yVariable == null ? null : yVariable.getName();
@ -74,10 +79,26 @@ public class XyFunction {
return imag;
}
@Override
public int getArity() {
return arity;
}
@Override
public double eval() {
return evaluator.eval();
}
@Override
public double eval(double x) {
return evaluator.eval(x);
}
@Override
public double eval(double x, double y) {
return evaluator.eval(x, y);
}
@NotNull
public Generic getExpression() {
return expression;
@ -113,8 +134,7 @@ public class XyFunction {
return yVariableName;
}
@Override
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof XyFunction)) return false;
@ -130,4 +150,85 @@ public class XyFunction {
public int hashCode() {
return id.hashCode();
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
private static abstract class AbstractEvaluator implements FunctionEvaluator {
@NotNull
protected final XyFunction xyFunction;
@Nullable
private Double constant = null;
public AbstractEvaluator(@NotNull XyFunction xyFunction) {
this.xyFunction = xyFunction;
}
@Override
public final double eval() {
if (constant == null) {
constant = eval0();
}
return constant;
}
protected abstract double eval0();
@Override
public final int getArity() {
return xyFunction.getArity();
}
}
private static class RealEvaluator extends AbstractEvaluator {
private RealEvaluator(@NotNull XyFunction xyFunction) {
super(xyFunction);
}
@Override
public double eval0() {
return PlotUtils.calculatorExpression(xyFunction.expression).realPart();
}
@Override
public double eval(double x) {
return PlotUtils.calculatorExpression(xyFunction.expression, xyFunction.xVariable, x).realPart();
}
@Override
public double eval(double x, double y) {
return PlotUtils.calculatorExpression(xyFunction.expression, xyFunction.xVariable, x, xyFunction.yVariable, y).realPart();
}
}
private static class ImaginaryEvaluator extends AbstractEvaluator {
private ImaginaryEvaluator(@NotNull XyFunction xyFunction) {
super(xyFunction);
}
@Override
public double eval0() {
return PlotUtils.calculatorExpression(xyFunction.expression).imaginaryPart();
}
@Override
public double eval(double x) {
return PlotUtils.calculatorExpression(xyFunction.expression, xyFunction.xVariable, x).imaginaryPart();
}
@Override
public double eval(double x, double y) {
return PlotUtils.calculatorExpression(xyFunction.expression, xyFunction.xVariable, x, xyFunction.yVariable, y).imaginaryPart();
}
}
}