Plotter improvements
This commit is contained in:
parent
a49ca358af
commit
600b454eef
File diff suppressed because it is too large
Load Diff
@ -1,157 +1,340 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
* For more information, please, contact se.solovyev@gmail.com
|
||||||
* or visit http://se.solovyev.org
|
* or visit http://se.solovyev.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.solovyev.android.calculator.plot;
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
import jscl.math.Expression;
|
import android.util.Log;
|
||||||
import jscl.math.Generic;
|
import jscl.math.Expression;
|
||||||
import jscl.math.JsclInteger;
|
import jscl.math.Generic;
|
||||||
import jscl.math.NumericWrapper;
|
import jscl.math.JsclInteger;
|
||||||
import jscl.math.function.Constant;
|
import jscl.math.NumericWrapper;
|
||||||
import jscl.math.numeric.Complex;
|
import jscl.math.function.Constant;
|
||||||
import jscl.math.numeric.Numeric;
|
import jscl.math.numeric.Complex;
|
||||||
import jscl.math.numeric.Real;
|
import jscl.math.numeric.Numeric;
|
||||||
import org.achartengine.util.MathHelper;
|
import jscl.math.numeric.Real;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.achartengine.util.MathHelper;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
/**
|
|
||||||
* User: serso
|
/**
|
||||||
* Date: 12/5/11
|
* User: serso
|
||||||
* Time: 8:58 PM
|
* Date: 12/5/11
|
||||||
*/
|
* Time: 8:58 PM
|
||||||
public final class PlotUtils {
|
*/
|
||||||
|
public final class PlotUtils {
|
||||||
private static final double MAX_Y_DIFF = Math.pow(10, 6);
|
|
||||||
|
private static final double MAX_Y_DIFF = 1;
|
||||||
// not intended for instantiation
|
private static final double MAX_X_DIFF = 1;
|
||||||
private PlotUtils() {
|
|
||||||
throw new AssertionError();
|
// not intended for instantiation
|
||||||
}
|
private PlotUtils() {
|
||||||
|
throw new AssertionError();
|
||||||
public static boolean addXY(double minValue,
|
}
|
||||||
double maxValue,
|
|
||||||
@NotNull Generic expression,
|
public static boolean addXY(double minValue,
|
||||||
@NotNull Constant variable,
|
double maxValue,
|
||||||
@NotNull MyXYSeries realSeries,
|
@NotNull Generic expression,
|
||||||
@Nullable MyXYSeries imagSeries,
|
@NotNull Constant variable,
|
||||||
boolean addExtra,
|
@NotNull MyXYSeries realSeries,
|
||||||
int numberOfSteps) throws ArithmeticException {
|
@Nullable MyXYSeries imagSeries,
|
||||||
|
boolean addExtra,
|
||||||
boolean imagExists = false;
|
int numberOfSteps) throws ArithmeticException {
|
||||||
|
|
||||||
double min = Math.min(minValue, maxValue);
|
boolean imagExists = false;
|
||||||
double max = Math.max(minValue, maxValue);
|
|
||||||
double dist = max - min;
|
double min = Math.min(minValue, maxValue);
|
||||||
if (addExtra) {
|
double max = Math.max(minValue, maxValue);
|
||||||
min = min - dist;
|
double dist = max - min;
|
||||||
max = max + dist;
|
if (addExtra) {
|
||||||
}
|
min = min - dist;
|
||||||
|
max = max + dist;
|
||||||
final double step = Math.max( dist / numberOfSteps, 0.000000001);
|
}
|
||||||
|
|
||||||
Double prevRealY = null;
|
final double eps = 0.000000001;
|
||||||
Double prevX = null;
|
|
||||||
Double prevImagY = null;
|
final double defaultStep = Math.max(dist / numberOfSteps, eps);
|
||||||
|
double step = defaultStep;
|
||||||
double x = min;
|
|
||||||
while (x <= max) {
|
final Point real = new Point();
|
||||||
|
final Point imag = new Point();
|
||||||
boolean needToCalculateRealY = realSeries.needToAdd(step, x);
|
|
||||||
|
double x = min;
|
||||||
if (needToCalculateRealY) {
|
|
||||||
final Complex c = calculatorExpression(expression, variable, x);
|
while (x <= max) {
|
||||||
Double y = prepareY(c.realPart());
|
|
||||||
if (y != null) {
|
boolean needToCalculateRealY = realSeries.needToAdd(eps, x);
|
||||||
addSingularityPoint(realSeries, prevX, x, prevRealY, y);
|
|
||||||
realSeries.add(x, y);
|
if (needToCalculateRealY) {
|
||||||
prevRealY = y;
|
final Complex c = calculatorExpression(expression, variable, x);
|
||||||
prevX = x;
|
Double y = prepareY(c.realPart());
|
||||||
}
|
|
||||||
|
if (y != null) {
|
||||||
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(step, x);
|
real.moveToNextPoint(x, y);
|
||||||
if (needToCalculateImagY) {
|
addSingularityPoint(realSeries, real);
|
||||||
y = prepareY(c.imaginaryPart());
|
realSeries.add(x, y);
|
||||||
if (y != null) {
|
}
|
||||||
addSingularityPoint(imagSeries, prevX, x, prevImagY, y);
|
|
||||||
imagSeries.add(x, y);
|
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(eps, x);
|
||||||
prevImagY = y;
|
if (needToCalculateImagY) {
|
||||||
prevX = x;
|
y = prepareY(c.imaginaryPart());
|
||||||
}
|
if (y != null) {
|
||||||
if (c.imaginaryPart() != 0d) {
|
imag.moveToNextPoint(x, y);
|
||||||
imagExists = true;
|
addSingularityPoint(imagSeries, imag);
|
||||||
}
|
imagSeries.add(x, y);
|
||||||
}
|
}
|
||||||
} else {
|
if (c.imaginaryPart() != 0d) {
|
||||||
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(step, x);
|
imagExists = true;
|
||||||
if (needToCalculateImagY) {
|
}
|
||||||
final Complex c = calculatorExpression(expression, variable, x);
|
}
|
||||||
Double y = prepareY(c.imaginaryPart());
|
} else {
|
||||||
if (y != null) {
|
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(eps, x);
|
||||||
addSingularityPoint(imagSeries, prevX, x, prevImagY, y);
|
if (needToCalculateImagY) {
|
||||||
imagSeries.add(x, y);
|
final Complex c = calculatorExpression(expression, variable, x);
|
||||||
prevImagY = y;
|
Double y = prepareY(c.imaginaryPart());
|
||||||
prevX = x;
|
if (y != null) {
|
||||||
}
|
imag.moveToNextPoint(x, y);
|
||||||
if (c.imaginaryPart() != 0d) {
|
addSingularityPoint(imagSeries, imag);
|
||||||
imagExists = true;
|
imagSeries.add(x, y);
|
||||||
}
|
}
|
||||||
}
|
if (c.imaginaryPart() != 0d) {
|
||||||
}
|
imagExists = true;
|
||||||
|
}
|
||||||
x += step;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return imagExists;
|
step = updateStep(real, step, defaultStep / 2);
|
||||||
}
|
|
||||||
|
if (real.isX2Defined()) {
|
||||||
@NotNull
|
x = real.getX2() + step;
|
||||||
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant variable, double x) {
|
} else {
|
||||||
return unwrap(expression.substitute(variable, Expression.valueOf(x)).numeric());
|
x += step;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public static void addSingularityPoint(@NotNull MyXYSeries series, @Nullable Double prevX, @NotNull Double x, @Nullable Double prevY, @NotNull Double y) {
|
|
||||||
if (prevX != null && prevY != null) {
|
return imagExists;
|
||||||
// y or prevY should be more than 1d because if they are too small false singularity may occur (e.g., 1/0.000000000000000001)
|
}
|
||||||
if ( (Math.abs(y) >= 1d && Math.abs(prevY / y) > MAX_Y_DIFF) || (Math.abs(prevY) >= 1d && Math.abs(y / prevY) > MAX_Y_DIFF)) {
|
|
||||||
//Log.d(CalculatorPlotActivity.class.getName(), "Singularity! Prev point: (" + prevX + ", " + prevY + "), current point: (" +x+ ", " + y +")" );
|
private static class Point {
|
||||||
//Log.d(CalculatorPlotActivity.class.getName(), String.valueOf(prevX + Math.abs(x - prevX) / 2) + ", null");
|
private static final double DEFAULT = Double.MIN_VALUE;
|
||||||
series.add(prevX + Math.abs(x - prevX) / 2, MathHelper.NULL_VALUE);
|
|
||||||
}
|
private double x0 = DEFAULT;
|
||||||
}
|
private double x1 = DEFAULT;
|
||||||
}
|
private double x2 = DEFAULT;
|
||||||
|
|
||||||
@Nullable
|
private double y0 = DEFAULT;
|
||||||
public static Double prepareY(double y) {
|
private double y1 = DEFAULT;
|
||||||
if (Double.isNaN(y)) {
|
private double y2 = DEFAULT;
|
||||||
return null;
|
|
||||||
} else {
|
private Point() {
|
||||||
return y;
|
}
|
||||||
}
|
|
||||||
}
|
public void moveToNextPoint(double x, double y) {
|
||||||
|
if ( this.x2 == x ) {
|
||||||
@NotNull
|
return;
|
||||||
public static Complex unwrap(@Nullable Generic numeric) {
|
}
|
||||||
if (numeric instanceof JsclInteger) {
|
|
||||||
return Complex.valueOf(((JsclInteger) numeric).intValue(), 0d);
|
this.x0 = this.x1;
|
||||||
} else if (numeric instanceof NumericWrapper) {
|
this.x1 = this.x2;
|
||||||
return unwrap(((NumericWrapper) numeric).content());
|
this.x2 = x;
|
||||||
} else {
|
|
||||||
throw new ArithmeticException();
|
this.y0 = this.y1;
|
||||||
}
|
this.y1 = this.y2;
|
||||||
}
|
this.y2 = y;
|
||||||
|
}
|
||||||
@NotNull
|
|
||||||
public static Complex unwrap(@Nullable Numeric content) {
|
public boolean isFullyDefined() {
|
||||||
if (content instanceof Real) {
|
return x0 != DEFAULT && x1 != DEFAULT && x2 != DEFAULT && y0 != DEFAULT && y1 != DEFAULT && y2 != DEFAULT;
|
||||||
return Complex.valueOf(((Real) content).doubleValue(), 0d);
|
}
|
||||||
} else if (content instanceof Complex) {
|
|
||||||
return ((Complex) content);
|
public double getDx2() {
|
||||||
} else {
|
return x2 - x1;
|
||||||
throw new ArithmeticException();
|
}
|
||||||
}
|
|
||||||
}
|
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, @NotNull Constant variable, double x) {
|
||||||
|
return unwrap(expression.substitute(variable, Expression.valueOf(x)).numeric());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
throw new ArithmeticException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user