new plotter
This commit is contained in:
parent
93c5e2a093
commit
b60b576433
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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,7 +134,6 @@ public class XyFunction {
|
||||
return yVariableName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user