new plotter
This commit is contained in:
parent
4f2c2701c0
commit
bbdec030fe
@ -11,7 +11,6 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
||||||
import org.achartengine.ChartFactory;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.solovyev.android.AndroidUtils2;
|
import org.solovyev.android.AndroidUtils2;
|
||||||
@ -98,7 +97,6 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
|
|||||||
|
|
||||||
public static void plotGraph(@NotNull final Context context){
|
public static void plotGraph(@NotNull final Context context){
|
||||||
final Intent intent = new Intent();
|
final Intent intent = new Intent();
|
||||||
intent.putExtra(ChartFactory.TITLE, context.getString(R.string.c_graph));
|
|
||||||
intent.setClass(context, CalculatorPlotActivity.class);
|
intent.setClass(context, CalculatorPlotActivity.class);
|
||||||
AndroidUtils2.addFlags(intent, false, context);
|
AndroidUtils2.addFlags(intent, false, context);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 1/18/13
|
||||||
|
* Time: 9:03 PM
|
||||||
|
*/
|
||||||
|
public abstract class AbstractGraphCalculator implements GraphCalculator {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected final GraphData next = GraphData.newEmptyInstance();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final GraphData endGraph = GraphData.newEmptyInstance();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final GraphData startGraph = GraphData.newEmptyInstance();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void computeGraph(@NotNull XyFunction f, float xMin, float xMax, @NotNull GraphData graph, @NotNull GraphsData graphsData, @NotNull Graph2dDimensions dimensions) {
|
||||||
|
if (f.getArity() == 0) {
|
||||||
|
final float v = (float) f.eval();
|
||||||
|
graph.clear();
|
||||||
|
graph.push(xMin, v);
|
||||||
|
graph.push(xMax, v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float yMin = graphsData.getLastYMin();
|
||||||
|
float yMax = graphsData.getLastYMin();
|
||||||
|
|
||||||
|
// prepare graph
|
||||||
|
if (!graph.empty()) {
|
||||||
|
if (xMin >= graphsData.getLastXMin()) {
|
||||||
|
// |------[---erased---|------data----|---erased--]------ old data
|
||||||
|
// |-------------------[------data----]------------------ new data
|
||||||
|
// xMin xMax
|
||||||
|
//
|
||||||
|
// OR
|
||||||
|
//
|
||||||
|
// |------[---erased---|------data----]----------- old data
|
||||||
|
// |-------------------[------data----<---->]----- new data
|
||||||
|
// xMin xMax
|
||||||
|
graph.eraseBefore(xMin);
|
||||||
|
if ( xMax <= graphsData.getLastXMax() ) {
|
||||||
|
graph.eraseAfter(xMax);
|
||||||
|
// nothing to compute
|
||||||
|
} else {
|
||||||
|
xMin = graph.getLastX();
|
||||||
|
compute(f, xMin, xMax, yMin, yMax, endGraph, dimensions);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// |--------------------[-----data----|---erased----]-- old data
|
||||||
|
// |------[<------------>-----data----]---------------- new data
|
||||||
|
// xMin xMax
|
||||||
|
//
|
||||||
|
// OR
|
||||||
|
//
|
||||||
|
// |--------------------[------data--]----|----------- old data
|
||||||
|
// |-------[<----------->------data--<--->]-----------new data
|
||||||
|
// xMin xMax
|
||||||
|
|
||||||
|
if ( xMax <= graphsData.getLastXMax() ) {
|
||||||
|
graph.eraseAfter(xMax);
|
||||||
|
xMax = graph.getFirstX();
|
||||||
|
compute(f, xMin, xMax, yMin, yMax, startGraph, dimensions);
|
||||||
|
} else {
|
||||||
|
compute(f, xMin, graph.getFirstX(), yMin, yMax, startGraph, dimensions);
|
||||||
|
compute(f, graph.getLastX(), xMax, yMin, yMax, endGraph, dimensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
compute(f, xMin, xMax, yMin, yMax, graph, dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endGraph.empty()) {
|
||||||
|
// first add ending because it's fast
|
||||||
|
graph.append(endGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!startGraph.empty()) {
|
||||||
|
startGraph.append(graph);
|
||||||
|
graph.swap(startGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
next.clear();
|
||||||
|
endGraph.clear();
|
||||||
|
startGraph.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void compute(@NotNull XyFunction f,
|
||||||
|
float xMin,
|
||||||
|
float xMax,
|
||||||
|
float yMin,
|
||||||
|
float yMax,
|
||||||
|
@NotNull GraphData graph,
|
||||||
|
@NotNull Graph2dDimensions dimensions);
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
package org.solovyev.android.calculator.plot;
|
|
||||||
|
|
||||||
import org.javia.arity.Function;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 1/5/13
|
|
||||||
* Time: 7:35 PM
|
|
||||||
*/
|
|
||||||
public class ArityPlotFunction {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Function function;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private PlotLineDef lineDef;
|
|
||||||
|
|
||||||
private ArityPlotFunction() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static ArityPlotFunction newInstance(@NotNull Function function) {
|
|
||||||
return newInstance(function, PlotLineDef.newDefaultInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static ArityPlotFunction newInstance(@NotNull Function function, @NotNull PlotLineDef lineDef) {
|
|
||||||
final ArityPlotFunction result = new ArityPlotFunction();
|
|
||||||
|
|
||||||
result.function = function;
|
|
||||||
result.lineDef = lineDef;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Function getFunction() {
|
|
||||||
return function;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public PlotLineDef getLineDef() {
|
|
||||||
return lineDef;
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.solovyev.common.math.Point2d;
|
import org.solovyev.common.math.Point2d;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -46,10 +45,6 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
**********************************************************************
|
**********************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// view width and height in pixels
|
|
||||||
private int widthPxs;
|
|
||||||
private int heightPxs;
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final Matrix matrix = new Matrix();
|
private final Matrix matrix = new Matrix();
|
||||||
|
|
||||||
@ -66,25 +61,8 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private GraphViewHelper graphViewHelper = GraphViewHelper.newDefaultInstance();
|
private GraphViewHelper graphViewHelper = GraphViewHelper.newDefaultInstance();
|
||||||
|
|
||||||
private final GraphData next = GraphData.newEmptyInstance();
|
|
||||||
|
|
||||||
private final GraphData endGraph = GraphData.newEmptyInstance();
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private List<GraphData> graphs = new ArrayList<GraphData>(graphViewHelper.getFunctionPlotDefs().size());
|
private final GraphsData graphsData = new GraphsData(this);
|
||||||
|
|
||||||
// current position of camera in graph coordinates
|
|
||||||
private float x0;
|
|
||||||
private float y0;
|
|
||||||
|
|
||||||
// graph width in function units (NOT screen pixels)
|
|
||||||
private float gWidth = 20;
|
|
||||||
|
|
||||||
private float lastXMin;
|
|
||||||
|
|
||||||
private float lastYMin;
|
|
||||||
private float lastYMax;
|
|
||||||
|
|
||||||
private float lastTouchXPxs = NO_TOUCH;
|
private float lastTouchXPxs = NO_TOUCH;
|
||||||
private float lastTouchYPxs = NO_TOUCH;
|
private float lastTouchYPxs = NO_TOUCH;
|
||||||
@ -101,6 +79,21 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private Scroller scroller;
|
private Scroller scroller;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Graph2dDimensions dimensions = new Graph2dDimensions(this);
|
||||||
|
|
||||||
|
private final GraphCalculator graphCalculator = new GraphCalculatorImpl();
|
||||||
|
|
||||||
|
private boolean mDrawn = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* CONSTRUCTORS
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
public CalculatorGraph2dView(Context context, AttributeSet attrs) {
|
public CalculatorGraph2dView(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
init(context);
|
init(context);
|
||||||
@ -119,39 +112,21 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
paint.setAntiAlias(false);
|
paint.setAntiAlias(false);
|
||||||
textPaint.setAntiAlias(true);
|
textPaint.setAntiAlias(true);
|
||||||
|
|
||||||
widthPxs = this.getWidth();
|
dimensions.setViewDimensions(this);
|
||||||
heightPxs = this.getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Bitmap captureScreenshot() {
|
|
||||||
final Bitmap result = Bitmap.createBitmap(widthPxs, heightPxs, Bitmap.Config.RGB_565);
|
|
||||||
Canvas canvas = new Canvas(result);
|
|
||||||
onDraw(canvas);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setXRange(float xMin, float xMax) {
|
public void init(@NotNull PlotViewDef plotViewDef) {
|
||||||
this.gWidth = xMax - xMin;
|
this.graphViewHelper = GraphViewHelper.newInstance(plotViewDef, Collections.<PlotFunction>emptyList());
|
||||||
this.x0 = xMin + gWidth / 2;
|
|
||||||
this.y0 = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearAllGraphs() {
|
/*
|
||||||
for (GraphData graph : graphs) {
|
**********************************************************************
|
||||||
graph.clear();
|
*
|
||||||
}
|
* METHODS
|
||||||
|
*
|
||||||
while (graphViewHelper.getFunctionPlotDefs().size() > graphs.size()) {
|
**********************************************************************
|
||||||
graphs.add(GraphData.newEmptyInstance());
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(@NotNull FunctionViewDef functionViewDef) {
|
|
||||||
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<PlotFunction>emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPlotFunctions(@NotNull List<PlotFunction> plotFunctions) {
|
public void setPlotFunctions(@NotNull List<PlotFunction> plotFunctions) {
|
||||||
|
|
||||||
@ -163,8 +138,31 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.graphViewHelper = this.graphViewHelper.copy(plotFunctions);
|
this.graphViewHelper = this.graphViewHelper.copy(plotFunctions);
|
||||||
clearAllGraphs();
|
invalidateGraphs();
|
||||||
invalidate();
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public List<PlotFunction> getPlotFunctions() {
|
||||||
|
return this.graphViewHelper.getPlotFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Bitmap captureScreenshot() {
|
||||||
|
final Bitmap result = Bitmap.createBitmap(dimensions.getVWidthPxs(), dimensions.getVHeightPxs(), Bitmap.Config.RGB_565);
|
||||||
|
onDraw(new Canvas(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateGraphs() {
|
||||||
|
if (mDrawn) {
|
||||||
|
mDrawn = false;
|
||||||
|
|
||||||
|
graphsData.clear();
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
@ -179,149 +177,22 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void onSizeChanged(int w, int h, int ow, int oh) {
|
protected void onSizeChanged(int w, int h, int ow, int oh) {
|
||||||
widthPxs = w;
|
dimensions.setViewDimensions(w, h);
|
||||||
heightPxs = h;
|
|
||||||
clearAllGraphs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onDraw(Canvas canvas) {
|
protected void onDraw(@NotNull Canvas canvas) {
|
||||||
if (graphViewHelper.getFunctionPlotDefs().size() == 0) {
|
if (!graphViewHelper.getPlotFunctions().isEmpty()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (scroller.computeScrollOffset()) {
|
|
||||||
final float ratio = getRatio();
|
|
||||||
x0 = scroller.getCurrX() * ratio;
|
|
||||||
y0 = scroller.getCurrY() * ratio;
|
|
||||||
if (!scroller.isFinished()) {
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
drawGraph(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// distance from (x,y) to the line (x1,y1) to (x2,y2), squared, multiplied by 4
|
if (scroller.computeScrollOffset()) {
|
||||||
/*
|
final float ratio = dimensions.getGraphToViewRatio();
|
||||||
private float distance(float x1, float y1, float x2, float y2, float x, float y) {
|
dimensions.setXY(scroller.getCurrX() * ratio, scroller.getCurrY() * ratio);
|
||||||
float dx = x2 - x1;
|
if (!scroller.isFinished()) {
|
||||||
float dy = y2 - y1;
|
invalidate();
|
||||||
float mx = x - x1;
|
|
||||||
float my = y - y1;
|
|
||||||
float up = dx*my - dy*mx;
|
|
||||||
return up*up*4/(dx*dx + dy*dy);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// distance as above when x==(x1+x2)/2.
|
|
||||||
private float distance2(float x1, float y1, float x2, float y2, float y) {
|
|
||||||
final float dx = x2 - x1;
|
|
||||||
final float dy = y2 - y1;
|
|
||||||
final float up = dx * (y1 + y2 - y - y);
|
|
||||||
return up * up / (dx * dx + dy * dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void computeGraph(@NotNull XyFunction f,
|
|
||||||
float xMin,
|
|
||||||
float xMax,
|
|
||||||
float yMin,
|
|
||||||
float yMax,
|
|
||||||
@NotNull GraphData graph) {
|
|
||||||
if (f.getArity() == 0) {
|
|
||||||
final float v = (float) f.eval();
|
|
||||||
graph.clear();
|
|
||||||
graph.push(xMin, v);
|
|
||||||
graph.push(xMax, v);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare graph
|
|
||||||
if (!graph.empty()) {
|
|
||||||
if (xMin >= lastXMin) {
|
|
||||||
graph.eraseBefore(xMin);
|
|
||||||
} else {
|
|
||||||
graph.eraseAfter(xMax);
|
|
||||||
xMax = Math.min(xMax, graph.firstX());
|
|
||||||
graph.swap(endGraph);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (graph.empty()) {
|
|
||||||
graph.push(xMin, (float)f.eval(xMin));
|
|
||||||
}
|
|
||||||
|
|
||||||
final float ratio = getRatio();
|
|
||||||
final float maxStep = 15.8976f * ratio;
|
|
||||||
final float minStep = .05f * ratio;
|
|
||||||
float ythresh = ratio;
|
|
||||||
ythresh = ythresh * ythresh;
|
|
||||||
|
|
||||||
|
|
||||||
float leftX, leftY;
|
|
||||||
float rightX = graph.topX(), rightY = graph.topY();
|
|
||||||
int nEval = 1;
|
|
||||||
while (true) {
|
|
||||||
leftX = rightX;
|
|
||||||
leftY = rightY;
|
|
||||||
if (leftX > xMax) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (next.empty()) {
|
|
||||||
float x = leftX + maxStep;
|
|
||||||
next.push(x, (float) f.eval(x));
|
|
||||||
++nEval;
|
|
||||||
}
|
|
||||||
rightX = next.topX();
|
|
||||||
rightY = next.topY();
|
|
||||||
next.pop();
|
|
||||||
|
|
||||||
if (leftY != leftY && rightY != rightY) { // NaN
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float dx = rightX - leftX;
|
|
||||||
float middleX = (leftX + rightX) / 2;
|
|
||||||
float middleY = (float) f.eval(middleX);
|
|
||||||
++nEval;
|
|
||||||
boolean middleIsOutside = (middleY < leftY && middleY < rightY) || (leftY < middleY && rightY < middleY);
|
|
||||||
if (dx < minStep) {
|
|
||||||
// Calculator.log("minStep");
|
|
||||||
if (middleIsOutside) {
|
|
||||||
graph.push(rightX, Float.NaN);
|
|
||||||
}
|
|
||||||
graph.push(rightX, rightY);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (middleIsOutside && ((leftY < yMin && rightY > yMax) || (leftY > yMax && rightY < yMin))) {
|
|
||||||
graph.push(rightX, Float.NaN);
|
|
||||||
graph.push(rightX, rightY);
|
|
||||||
// Calculator.log("+-inf");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!middleIsOutside) {
|
|
||||||
/*
|
|
||||||
float diff = leftY + rightY - middleY - middleY;
|
|
||||||
float dy = rightY - leftY;
|
|
||||||
float dx2 = dx*dx;
|
|
||||||
float distance = dx2*diff*diff/(dx2+dy*dy);
|
|
||||||
*/
|
|
||||||
// Calculator.log("" + dx + ' ' + leftY + ' ' + middleY + ' ' + rightY + ' ' + distance + ' ' + ythresh);
|
|
||||||
if (distance2(leftX, leftY, rightX, rightY, middleY) < ythresh) {
|
|
||||||
graph.push(rightX, rightY);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next.push(rightX, rightY);
|
|
||||||
next.push(middleX, middleY);
|
|
||||||
rightX = leftX;
|
|
||||||
rightY = leftY;
|
|
||||||
}
|
|
||||||
if (!endGraph.empty()) {
|
|
||||||
graph.append(endGraph);
|
|
||||||
}
|
|
||||||
long t2 = System.currentTimeMillis();
|
|
||||||
// Calculator.log("graph points " + graph.size + " evals " + nEval + " time " + (t2-t1));
|
|
||||||
|
|
||||||
next.clear();
|
drawGraph(canvas);
|
||||||
endGraph.clear();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void graphToPath(@NotNull GraphData graph, @NotNull Path path) {
|
private static void graphToPath(@NotNull GraphData graph, @NotNull Path path) {
|
||||||
@ -352,51 +223,30 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float getStep(float width) {
|
|
||||||
float f = 1;
|
|
||||||
while (width / f > TICKS_COUNT) {
|
|
||||||
f *= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (width / f < TICKS_COUNT / 10) {
|
|
||||||
f /= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
final float r = width / f;
|
|
||||||
if (r < TICKS_COUNT / 5) {
|
|
||||||
return f / 5;
|
|
||||||
} else if (r < TICKS_COUNT / 2) {
|
|
||||||
return f / 2;
|
|
||||||
} else {
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawGraph(@NotNull Canvas canvas) {
|
private void drawGraph(@NotNull Canvas canvas) {
|
||||||
final float graphHeight = getGraphHeight();
|
mDrawn = true;
|
||||||
|
|
||||||
final float xMin = getXMin();
|
final float graphHeight = dimensions.getGraphHeight();
|
||||||
final float xMax = getXMax(xMin);
|
|
||||||
|
|
||||||
final float yMin = getYMin(graphHeight);
|
final float xMin = dimensions.getXMin();
|
||||||
final float yMax = getYMax(graphHeight, yMin);
|
final float xMax = dimensions.getXMax(xMin);
|
||||||
|
|
||||||
if (yMin < lastYMin || yMax > lastYMax) {
|
final float yMin = dimensions.getYMin(graphHeight);
|
||||||
float halfGraphHeight = graphHeight / 2;
|
final float yMax = dimensions.getYMax(graphHeight, yMin);
|
||||||
lastYMin = yMin - halfGraphHeight;
|
final float widthPxs = dimensions.getVWidthPxs();
|
||||||
lastYMax = yMax + halfGraphHeight;
|
final float heightPxs = dimensions.getVHeightPxs();
|
||||||
clearAllGraphs();
|
|
||||||
}
|
graphsData.checkBoundaries(graphHeight, yMin, yMax);
|
||||||
|
|
||||||
// set background
|
// set background
|
||||||
canvas.drawColor(graphViewHelper.getFunctionViewDef().getBackgroundColor());
|
canvas.drawColor(graphViewHelper.getPlotViewDef().getBackgroundColor());
|
||||||
|
|
||||||
// prepare paint
|
// prepare paint
|
||||||
paint.setStrokeWidth(0);
|
paint.setStrokeWidth(0);
|
||||||
paint.setAntiAlias(false);
|
paint.setAntiAlias(false);
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
|
||||||
final float ratio = getRatio();
|
final float ratio = dimensions.getGraphToViewRatio();
|
||||||
|
|
||||||
float x0px = -xMin / ratio;
|
float x0px = -xMin / ratio;
|
||||||
if (x0px < 25) {
|
if (x0px < 25) {
|
||||||
@ -413,16 +263,16 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final float tickStep = getStep(gWidth);
|
final float tickStep = getTickStep(dimensions.getGWidth());
|
||||||
final int tickDigits = countTickDigits(tickStep);
|
final int tickDigits = countTickDigits(tickStep);
|
||||||
|
|
||||||
{
|
{
|
||||||
// GRID
|
// GRID
|
||||||
|
|
||||||
paint.setPathEffect(new DashPathEffect(new float[]{5, 10}, 0));
|
paint.setPathEffect(new DashPathEffect(new float[]{5, 10}, 0));
|
||||||
paint.setColor(graphViewHelper.getFunctionViewDef().getGridColor());
|
paint.setColor(graphViewHelper.getPlotViewDef().getGridColor());
|
||||||
|
|
||||||
textPaint.setColor(graphViewHelper.getFunctionViewDef().getAxisLabelsColor());
|
textPaint.setColor(graphViewHelper.getPlotViewDef().getAxisLabelsColor());
|
||||||
textPaint.setTextSize(12);
|
textPaint.setTextSize(12);
|
||||||
textPaint.setTextAlign(Paint.Align.CENTER);
|
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
|
||||||
@ -461,7 +311,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
{
|
{
|
||||||
// AXIS
|
// AXIS
|
||||||
|
|
||||||
paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor());
|
paint.setColor(graphViewHelper.getPlotViewDef().getAxisColor());
|
||||||
canvas.drawLine(x0px, 0, x0px, heightPxs, paint);
|
canvas.drawLine(x0px, 0, x0px, heightPxs, paint);
|
||||||
canvas.drawLine(0, y0px, widthPxs, y0px, paint);
|
canvas.drawLine(0, y0px, widthPxs, y0px, paint);
|
||||||
}
|
}
|
||||||
@ -471,13 +321,13 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
|
|
||||||
if (lastTouchXPxs != NO_TOUCH && lastTouchYPxs != NO_TOUCH) {
|
if (lastTouchXPxs != NO_TOUCH && lastTouchYPxs != NO_TOUCH) {
|
||||||
|
|
||||||
paint.setColor(graphViewHelper.getFunctionViewDef().getGridColor());
|
paint.setColor(graphViewHelper.getPlotViewDef().getGridColor());
|
||||||
paint.setAlpha(100);
|
paint.setAlpha(100);
|
||||||
|
|
||||||
canvas.drawLine(lastTouchXPxs, 0, lastTouchXPxs, heightPxs, paint);
|
canvas.drawLine(lastTouchXPxs, 0, lastTouchXPxs, heightPxs, paint);
|
||||||
canvas.drawLine(0, lastTouchYPxs, widthPxs, lastTouchYPxs, paint);
|
canvas.drawLine(0, lastTouchYPxs, widthPxs, lastTouchYPxs, paint);
|
||||||
|
|
||||||
final Point2d lastTouch = toGraphCoordinates(lastTouchXPxs, lastTouchYPxs);
|
final Point2d lastTouch = dimensions.toGraphCoordinates(lastTouchXPxs, lastTouchYPxs);
|
||||||
final String touchLabel = "[" + formatTick(lastTouch.getX(), tickDigits + 1) + ", " + formatTick(lastTouch.getY(), tickDigits + 1) + "]";
|
final String touchLabel = "[" + formatTick(lastTouch.getX(), tickDigits + 1) + ", " + formatTick(lastTouch.getY(), tickDigits + 1) + "]";
|
||||||
canvas.drawText(touchLabel, 0, touchLabel.length(), lastTouchXPxs - 40, lastTouchYPxs - 40, textPaint);
|
canvas.drawText(touchLabel, 0, touchLabel.length(), lastTouchXPxs - 40, lastTouchYPxs - 40, textPaint);
|
||||||
|
|
||||||
@ -488,7 +338,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
|
|
||||||
|
|
||||||
matrix.reset();
|
matrix.reset();
|
||||||
matrix.preTranslate(-this.x0, -this.y0);
|
matrix.preTranslate(-dimensions.getX0(), -dimensions.getY0());
|
||||||
matrix.postScale(1/ratio, -1/ratio);
|
matrix.postScale(1/ratio, -1/ratio);
|
||||||
matrix.postTranslate(widthPxs / 2, heightPxs / 2);
|
matrix.postTranslate(widthPxs / 2, heightPxs / 2);
|
||||||
|
|
||||||
@ -497,16 +347,17 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
{
|
{
|
||||||
//GRAPH
|
//GRAPH
|
||||||
|
|
||||||
final List<PlotFunction> functionPlotDefs = graphViewHelper.getFunctionPlotDefs();
|
final List<PlotFunction> functionPlotDefs = graphViewHelper.getPlotFunctions();
|
||||||
|
|
||||||
// create path once
|
// create path once
|
||||||
final Path path = new Path();
|
final Path path = new Path();
|
||||||
|
|
||||||
for (int i = 0; i < functionPlotDefs.size(); i++) {
|
for (int i = 0; i < functionPlotDefs.size(); i++) {
|
||||||
final PlotFunction fpd = functionPlotDefs.get(i);
|
final PlotFunction fpd = functionPlotDefs.get(i);
|
||||||
computeGraph(fpd.getXyFunction(), xMin, xMax, lastYMin, lastYMax, graphs.get(i));
|
|
||||||
|
|
||||||
graphToPath(graphs.get(i), path);
|
graphCalculator.computeGraph(fpd.getXyFunction(), xMin, xMax, graphsData.get(i), graphsData, dimensions);
|
||||||
|
|
||||||
|
graphToPath(graphsData.get(i), path);
|
||||||
|
|
||||||
path.transform(matrix);
|
path.transform(matrix);
|
||||||
|
|
||||||
@ -517,13 +368,14 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lastXMin = xMin;
|
graphsData.setLastXMin(xMin);
|
||||||
|
graphsData.setLastXMax(xMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
*
|
*
|
||||||
* TICK FORMAT
|
* TICKS
|
||||||
*
|
*
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
*/
|
*/
|
||||||
@ -557,6 +409,27 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static float getTickStep(float width) {
|
||||||
|
float f = 1;
|
||||||
|
while (width / f > TICKS_COUNT) {
|
||||||
|
f *= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (width / f < TICKS_COUNT / 10) {
|
||||||
|
f /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
final float r = width / f;
|
||||||
|
if (r < TICKS_COUNT / 5) {
|
||||||
|
return f / 5;
|
||||||
|
} else if (r < TICKS_COUNT / 2) {
|
||||||
|
return f / 2;
|
||||||
|
} else {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
*
|
*
|
||||||
@ -565,64 +438,32 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
**********************************************************************
|
**********************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private Point2d toGraphCoordinates(float xPxs, float yPxs) {
|
|
||||||
return new Point2d(xPxs * getRatio() + getXMin(), - (yPxs * getRatio() + getYMin()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// X
|
// X
|
||||||
|
|
||||||
public float getXMin() {
|
public float getXMin() {
|
||||||
return x0 - gWidth / 2;
|
return dimensions.getXMin();
|
||||||
}
|
|
||||||
|
|
||||||
private float getXMax(float minX) {
|
|
||||||
return minX + gWidth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getXMax() {
|
public float getXMax() {
|
||||||
return getXMax(getXMin());
|
return dimensions.getXMax();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Y
|
// Y
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getYMin() {
|
public float getYMin() {
|
||||||
return getYMin(getGraphHeight());
|
return dimensions.getYMin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public float getYMin(float graphHeight) {
|
|
||||||
return y0 - graphHeight / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getYMax() {
|
public float getYMax() {
|
||||||
final float graphHeight = getGraphHeight();
|
return dimensions.getYMax();
|
||||||
return getYMax(graphHeight, getYMin(graphHeight));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getYMax(float graphHeight, float yMin) {
|
@Override
|
||||||
return yMin + graphHeight;
|
public void setXRange(float xMin, float xMax) {
|
||||||
}
|
this.dimensions.setXRange(xMin, xMax);
|
||||||
|
|
||||||
private float getGraphHeight() {
|
|
||||||
return gWidth * getAspectRatio();
|
|
||||||
}
|
|
||||||
|
|
||||||
private float getRatio() {
|
|
||||||
if (widthPxs != 0) {
|
|
||||||
return gWidth / widthPxs;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float getAspectRatio() {
|
|
||||||
if (widthPxs != 0) {
|
|
||||||
return ((float)heightPxs) / widthPxs;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -647,13 +488,11 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
public void onZoom(boolean zoomIn) {
|
public void onZoom(boolean zoomIn) {
|
||||||
if (zoomIn) {
|
if (zoomIn) {
|
||||||
if (canZoomIn()) {
|
if (canZoomIn()) {
|
||||||
gWidth /= 2;
|
dimensions.setGWidth(dimensions.getGWidth() / 2);
|
||||||
invalidateGraphs();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (canZoomOut()) {
|
if (canZoomOut()) {
|
||||||
gWidth *= 2;
|
dimensions.setGWidth(dimensions.getGWidth() * 2);
|
||||||
invalidateGraphs();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zoomController.setZoomInEnabled(canZoomIn());
|
zoomController.setZoomInEnabled(canZoomIn());
|
||||||
@ -700,7 +539,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onTouchUp(float x, float y) {
|
public void onTouchUp(float x, float y) {
|
||||||
final float ratio = getRatio();
|
final float ratio = dimensions.getGraphToViewRatio();
|
||||||
|
|
||||||
lastTouchXPxs = NO_TOUCH;
|
lastTouchXPxs = NO_TOUCH;
|
||||||
lastTouchYPxs = NO_TOUCH;
|
lastTouchYPxs = NO_TOUCH;
|
||||||
@ -715,35 +554,26 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
} else if (asy < asx / 3) {
|
} else if (asy < asx / 3) {
|
||||||
sy = 0;
|
sy = 0;
|
||||||
}
|
}
|
||||||
scroller.fling(Math.round(x0 / ratio), Math.round(y0 / ratio), Math.round(sx), Math.round(sy), Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
scroller.fling(Math.round(dimensions.getX0() / ratio), Math.round(dimensions.getY0() / ratio), Math.round(sx), Math.round(sy), Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTouchZoomDown(float x1, float y1, float x2, float y2) {
|
public void onTouchZoomDown(float x1, float y1, float x2, float y2) {
|
||||||
zoomTracker.start(gWidth, x1, y1, x2, y2);
|
zoomTracker.start(dimensions.getGWidth(), x1, y1, x2, y2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTouchZoomMove(float x1, float y1, float x2, float y2) {
|
public void onTouchZoomMove(float x1, float y1, float x2, float y2) {
|
||||||
if (!zoomTracker.update(x1, y1, x2, y2)) {
|
if (!zoomTracker.update(x1, y1, x2, y2)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float targetGwidth = zoomTracker.value;
|
float targetGWidth = zoomTracker.value;
|
||||||
if (targetGwidth > .25f && targetGwidth < 200) {
|
if (targetGWidth > .25f && targetGWidth < 200) {
|
||||||
gWidth = targetGwidth;
|
dimensions.setGWidth(targetGWidth);
|
||||||
}
|
}
|
||||||
// scroll(-zoomTracker.moveX, zoomTracker.moveY);
|
|
||||||
invalidateGraphs();
|
|
||||||
// Calculator.log("zoom redraw");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void invalidateGraphs() {
|
|
||||||
clearAllGraphs();
|
|
||||||
lastYMin = lastYMax = 0;
|
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scroll(float deltaX, float deltaY) {
|
private void scroll(float deltaX, float deltaY) {
|
||||||
final float scale = gWidth / widthPxs;
|
final float scale = dimensions.getGWidth() / dimensions.getVWidthPxs();
|
||||||
float dx = deltaX * scale;
|
float dx = deltaX * scale;
|
||||||
float dy = deltaY * scale;
|
float dy = deltaY * scale;
|
||||||
final float adx = Math.abs(dx);
|
final float adx = Math.abs(dx);
|
||||||
@ -753,7 +583,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
|||||||
} else if (ady < adx / 3) {
|
} else if (ady < adx / 3) {
|
||||||
dy = 0;
|
dy = 0;
|
||||||
}
|
}
|
||||||
x0 += dx;
|
|
||||||
y0 += dy;
|
dimensions.increaseXY(dx, dy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
|
|||||||
graphView = new CalculatorGraph2dView(getActivity());
|
graphView = new CalculatorGraph2dView(getActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor()));
|
graphView.init(PlotViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor()));
|
||||||
//graphView.setXRange((float)plotBoundaries.getXMin(), (float)plotBoundaries.getXMax());
|
//graphView.setXRange((float)plotBoundaries.getXMin(), (float)plotBoundaries.getXMax());
|
||||||
graphView.setPlotFunctions(plotData.getFunctions());
|
graphView.setPlotFunctions(plotData.getFunctions());
|
||||||
|
|
||||||
@ -99,12 +99,4 @@ public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
**********************************************************************
|
|
||||||
*
|
|
||||||
* STATIC
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,165 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.solovyev.common.math.Point2d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 1/18/13
|
||||||
|
* Time: 7:59 PM
|
||||||
|
*/
|
||||||
|
public class Graph2dDimensions {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private GraphView graphView;
|
||||||
|
|
||||||
|
// view width and height in pixels
|
||||||
|
private int vWidthPxs;
|
||||||
|
private int vHeightPxs;
|
||||||
|
|
||||||
|
// current position of camera in graph coordinates
|
||||||
|
private float x0;
|
||||||
|
private float y0;
|
||||||
|
|
||||||
|
// graph width in function units (NOT screen pixels)
|
||||||
|
private float gWidth = 20;
|
||||||
|
|
||||||
|
public Graph2dDimensions(@NotNull GraphView graphView) {
|
||||||
|
this.graphView = graphView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* METHODS
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
Point2d toGraphCoordinates(float xPxs, float yPxs) {
|
||||||
|
return new Point2d(xPxs * getGraphToViewRatio() + getXMin(), - (yPxs * getGraphToViewRatio() + getYMin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// X
|
||||||
|
|
||||||
|
public float getXMin() {
|
||||||
|
return x0 - gWidth / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getXMax(float minX) {
|
||||||
|
return minX + gWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getXMax() {
|
||||||
|
return getXMax(getXMin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Y
|
||||||
|
|
||||||
|
public float getYMin() {
|
||||||
|
return getYMin(getGraphHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public float getYMin(float graphHeight) {
|
||||||
|
return y0 - graphHeight / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getYMax() {
|
||||||
|
final float graphHeight = getGraphHeight();
|
||||||
|
return getYMax(graphHeight, getYMin(graphHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getYMax(float graphHeight, float yMin) {
|
||||||
|
return yMin + graphHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getGraphHeight() {
|
||||||
|
return gWidth * getAspectRatio();
|
||||||
|
}
|
||||||
|
|
||||||
|
float getGraphToViewRatio() {
|
||||||
|
if (vWidthPxs != 0) {
|
||||||
|
return gWidth / vWidthPxs;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getAspectRatio() {
|
||||||
|
if (vWidthPxs != 0) {
|
||||||
|
return ((float) vHeightPxs) / vWidthPxs;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVWidthPxs() {
|
||||||
|
return vWidthPxs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVHeightPxs() {
|
||||||
|
return vHeightPxs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX0() {
|
||||||
|
return x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY0() {
|
||||||
|
return y0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getGWidth() {
|
||||||
|
return gWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* SETTERS
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void setXRange(float xMin, float xMax) {
|
||||||
|
this.gWidth = xMax - xMin;
|
||||||
|
this.x0 = xMin + gWidth / 2;
|
||||||
|
this.y0 = 0;
|
||||||
|
|
||||||
|
this.graphView.invalidateGraphs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewDimensions(@NotNull View view) {
|
||||||
|
this.vWidthPxs = view.getWidth();
|
||||||
|
this.vHeightPxs = view.getHeight();
|
||||||
|
|
||||||
|
this.graphView.invalidateGraphs();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setGWidth(float gWidth) {
|
||||||
|
this.gWidth = gWidth;
|
||||||
|
|
||||||
|
this.graphView.invalidateGraphs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewDimensions(int vWidthPxs, int vHeightPxs) {
|
||||||
|
this.vWidthPxs = vWidthPxs;
|
||||||
|
this.vHeightPxs = vHeightPxs;
|
||||||
|
|
||||||
|
this.graphView.invalidateGraphs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setXY(float x0, float y0) {
|
||||||
|
this.x0 = x0;
|
||||||
|
this.y0 = y0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseXY(float dx, float dy) {
|
||||||
|
this.x0 += dx;
|
||||||
|
this.y0 += dy;
|
||||||
|
}
|
||||||
|
}
|
@ -173,8 +173,8 @@ public class Graph3dView extends GLView implements GraphView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(@NotNull FunctionViewDef functionViewDef) {
|
public void init(@NotNull PlotViewDef plotViewDef) {
|
||||||
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<PlotFunction>emptyList());
|
this.graphViewHelper = GraphViewHelper.newInstance(plotViewDef, Collections.<PlotFunction>emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -191,7 +191,13 @@ public class Graph3dView extends GLView implements GraphView {
|
|||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public List<PlotFunction> getPlotFunctions() {
|
||||||
|
return this.graphViewHelper.getPlotFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setXRange(float xMin, float xMax) {
|
public void setXRange(float xMin, float xMax) {
|
||||||
//To change body of implemented methods use File | Settings | File Templates.
|
//To change body of implemented methods use File | Settings | File Templates.
|
||||||
}
|
}
|
||||||
@ -216,12 +222,17 @@ public class Graph3dView extends GLView implements GraphView {
|
|||||||
return 0; //To change body of implemented methods use File | Settings | File Templates.
|
return 0; //To change body of implemented methods use File | Settings | File Templates.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateGraphs() {
|
||||||
|
//To change body of implemented methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceCreated(GL10 gl, int width, int height) {
|
public void onSurfaceCreated(GL10 gl, int width, int height) {
|
||||||
gl.glDisable(GL10.GL_DITHER);
|
gl.glDisable(GL10.GL_DITHER);
|
||||||
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
|
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
|
||||||
|
|
||||||
final int backgroundColor = graphViewHelper.getFunctionViewDef().getBackgroundColor();
|
final int backgroundColor = graphViewHelper.getPlotViewDef().getBackgroundColor();
|
||||||
gl.glClearColor(Color.red(backgroundColor) / 255f, Color.green(backgroundColor) / 255f, Color.blue(backgroundColor) / 255f, Color.alpha(backgroundColor) / 255f);
|
gl.glClearColor(Color.red(backgroundColor) / 255f, Color.green(backgroundColor) / 255f, Color.blue(backgroundColor) / 255f, Color.alpha(backgroundColor) / 255f);
|
||||||
|
|
||||||
gl.glShadeModel(useHighQuality3d ? GL10.GL_SMOOTH : GL10.GL_FLAT);
|
gl.glShadeModel(useHighQuality3d ? GL10.GL_SMOOTH : GL10.GL_FLAT);
|
||||||
@ -248,8 +259,8 @@ public class Graph3dView extends GLView implements GraphView {
|
|||||||
}
|
}
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
ensureGraphsSize(gl);
|
ensureGraphsSize(gl);
|
||||||
for (int i = 0; i < graphViewHelper.getFunctionPlotDefs().size(); i++) {
|
for (int i = 0; i < graphViewHelper.getPlotFunctions().size(); i++) {
|
||||||
graphs.get(i).update(gl, graphViewHelper.getFunctionPlotDefs().get(i), zoomLevel);
|
graphs.get(i).update(gl, graphViewHelper.getPlotFunctions().get(i), zoomLevel);
|
||||||
|
|
||||||
}
|
}
|
||||||
isDirty = false;
|
isDirty = false;
|
||||||
@ -308,7 +319,7 @@ public class Graph3dView extends GLView implements GraphView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ensureGraphsSize(@NotNull GL11 gl) {
|
private void ensureGraphsSize(@NotNull GL11 gl) {
|
||||||
while (graphViewHelper.getFunctionPlotDefs().size() > graphs.size()) {
|
while (graphViewHelper.getPlotFunctions().size() > graphs.size()) {
|
||||||
graphs.add(new Graph3d(gl, useHighQuality3d));
|
graphs.add(new Graph3d(gl, useHighQuality3d));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 1/18/13
|
||||||
|
* Time: 8:58 PM
|
||||||
|
*/
|
||||||
|
public interface GraphCalculator {
|
||||||
|
|
||||||
|
void computeGraph(@NotNull XyFunction f,
|
||||||
|
float xMin,
|
||||||
|
float xMax,
|
||||||
|
@NotNull GraphData graph,
|
||||||
|
@NotNull GraphsData graphsData,
|
||||||
|
@NotNull Graph2dDimensions dimensions);
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 1/18/13
|
||||||
|
* Time: 8:58 PM
|
||||||
|
*/
|
||||||
|
public class GraphCalculatorImpl extends AbstractGraphCalculator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void compute(@NotNull XyFunction f,
|
||||||
|
float xMin,
|
||||||
|
float xMax,
|
||||||
|
float yMin,
|
||||||
|
float yMax,
|
||||||
|
@NotNull GraphData graph,
|
||||||
|
@NotNull Graph2dDimensions dimensions) {
|
||||||
|
graph.push(xMin, (float)f.eval(xMin));
|
||||||
|
|
||||||
|
final float ratio = dimensions.getGraphToViewRatio();
|
||||||
|
final float maxStep = 15.8976f * ratio;
|
||||||
|
final float minStep = .05f * ratio;
|
||||||
|
|
||||||
|
float yTheta = ratio;
|
||||||
|
yTheta = yTheta * yTheta;
|
||||||
|
|
||||||
|
|
||||||
|
float leftX;
|
||||||
|
float leftY;
|
||||||
|
|
||||||
|
float rightX = graph.getLastX();
|
||||||
|
float rightY = graph.getLastY();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
leftX = rightX;
|
||||||
|
leftY = rightY;
|
||||||
|
|
||||||
|
if (leftX > xMax) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next.empty()) {
|
||||||
|
float x = leftX + maxStep;
|
||||||
|
next.push(x, (float) f.eval(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
rightX = next.getLastX();
|
||||||
|
rightY = next.getLastY();
|
||||||
|
next.pop();
|
||||||
|
|
||||||
|
if (Float.isNaN(leftY) || Float.isNaN(rightY)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dx = rightX - leftX;
|
||||||
|
float middleX = (leftX + rightX) / 2;
|
||||||
|
float middleY = (float) f.eval(middleX);
|
||||||
|
|
||||||
|
boolean middleIsOutside = (middleY < leftY && middleY < rightY) || (leftY < middleY && rightY < middleY);
|
||||||
|
|
||||||
|
if (dx < minStep) {
|
||||||
|
// Calculator.log("minStep");
|
||||||
|
if (middleIsOutside) {
|
||||||
|
graph.push(rightX, Float.NaN);
|
||||||
|
}
|
||||||
|
graph.push(rightX, rightY);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (middleIsOutside && ((leftY < yMin && rightY > yMax) || (leftY > yMax && rightY < yMin))) {
|
||||||
|
graph.push(rightX, Float.NaN);
|
||||||
|
graph.push(rightX, rightY);
|
||||||
|
// Calculator.log("+-inf");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!middleIsOutside) {
|
||||||
|
if (distance2(leftX, leftY, rightX, rightY, middleY) < yTheta) {
|
||||||
|
graph.push(rightX, rightY);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next.push(rightX, rightY);
|
||||||
|
next.push(middleX, middleY);
|
||||||
|
rightX = leftX;
|
||||||
|
rightY = leftY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// distance as above when x==(x1+x2)/2.
|
||||||
|
private float distance2(float x1, float y1, float x2, float y2, float y) {
|
||||||
|
final float dx = x2 - x1;
|
||||||
|
final float dy = y2 - y1;
|
||||||
|
final float up = dx * (y1 + y2 - y - y);
|
||||||
|
return up * up / (dx * dx + dy * dy);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright (C) 2009 Mihai Preda
|
|
||||||
|
|
||||||
package org.solovyev.android.calculator.plot;
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -39,7 +37,7 @@ class GraphData {
|
|||||||
|
|
||||||
void push(float x, float y) {
|
void push(float x, float y) {
|
||||||
if (size >= allocatedSize) {
|
if (size >= allocatedSize) {
|
||||||
makeSpace(size + 1);
|
makeSpaceAtTheEnd(size + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
xs[size] = x;
|
xs[size] = x;
|
||||||
@ -47,9 +45,9 @@ class GraphData {
|
|||||||
++size;
|
++size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void makeSpace(int spaceSize) {
|
private void makeSpaceAtTheEnd(int newSize) {
|
||||||
int oldAllocatedSize = allocatedSize;
|
int oldAllocatedSize = allocatedSize;
|
||||||
while (spaceSize > allocatedSize) {
|
while (newSize > allocatedSize) {
|
||||||
allocatedSize += allocatedSize;
|
allocatedSize += allocatedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,19 +61,20 @@ class GraphData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float topX() {
|
|
||||||
|
float getLastX() {
|
||||||
return xs[size - 1];
|
return xs[size - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
float topY() {
|
float getLastY() {
|
||||||
return ys[size - 1];
|
return ys[size - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
float firstX() {
|
float getFirstX() {
|
||||||
return xs[0];
|
return xs[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
float firstY() {
|
float getFirstY() {
|
||||||
return ys[0];
|
return ys[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,36 +114,27 @@ class GraphData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int findPosAfter(float x, float y) {
|
int findPositionAfter(float x, float y) {
|
||||||
int pos = 0;
|
int position = 0;
|
||||||
while (pos < size && xs[pos] <= x) {
|
while (position < size && xs[position] <= x) {
|
||||||
++pos;
|
++position;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Float.isNaN(y)) {
|
if (Float.isNaN(y)) {
|
||||||
while (pos < size && ys[pos] != ys[pos]) {
|
while (position < size && Float.isNaN(ys[position])) {
|
||||||
++pos;
|
++position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Calculator.log("pos " + pos);
|
|
||||||
return pos;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
void append(GraphData d) {
|
void append(GraphData that) {
|
||||||
makeSpace(size + d.size);
|
makeSpaceAtTheEnd(size + that.size);
|
||||||
int pos = d.findPosAfter(xs[size - 1], ys[size - 1]);
|
int position = that.findPositionAfter(xs[size - 1], ys[size - 1]);
|
||||||
/*
|
System.arraycopy(that.xs, position, xs, size, that.size - position);
|
||||||
while (pos < d.size && d.xs[pos] <= last) {
|
System.arraycopy(that.ys, position, ys, size, that.size - position);
|
||||||
++pos;
|
size += that.size - position;
|
||||||
}
|
|
||||||
if (last != last) {
|
|
||||||
while (pos < d.size && d.ys[pos] != d.ys[pos]) {
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
System.arraycopy(d.xs, pos, xs, size, d.size - pos);
|
|
||||||
System.arraycopy(d.ys, pos, ys, size, d.size - pos);
|
|
||||||
size += d.size - pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// Copyright (C) 2009-2010 Mihai Preda
|
|
||||||
|
|
||||||
package org.solovyev.android.calculator.plot;
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -10,10 +8,13 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHandler.TouchHandlerListener {
|
public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHandler.TouchHandlerListener {
|
||||||
|
|
||||||
public void init(@NotNull FunctionViewDef functionViewDef);
|
public void init(@NotNull PlotViewDef plotViewDef);
|
||||||
|
|
||||||
public void setPlotFunctions(@NotNull List<PlotFunction> plotFunctions);
|
public void setPlotFunctions(@NotNull List<PlotFunction> plotFunctions);
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public List<PlotFunction> getPlotFunctions();
|
||||||
|
|
||||||
public void onPause();
|
public void onPause();
|
||||||
public void onResume();
|
public void onResume();
|
||||||
|
|
||||||
@ -30,6 +31,8 @@ public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHa
|
|||||||
|
|
||||||
float getYMax();
|
float getYMax();
|
||||||
|
|
||||||
|
void invalidateGraphs();
|
||||||
|
|
||||||
/* void increaseDensity();
|
/* void increaseDensity();
|
||||||
void decreaseDensity();*/
|
void decreaseDensity();*/
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ import java.util.List;
|
|||||||
public class GraphViewHelper {
|
public class GraphViewHelper {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private FunctionViewDef functionViewDef = FunctionViewDef.newDefaultInstance();
|
private PlotViewDef plotViewDef = PlotViewDef.newDefaultInstance();
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private List<PlotFunction> functionPlotDefs = Collections.emptyList();
|
private List<PlotFunction> plotFunctions = Collections.emptyList();
|
||||||
|
|
||||||
private GraphViewHelper() {
|
private GraphViewHelper() {
|
||||||
}
|
}
|
||||||
@ -27,12 +27,12 @@ public class GraphViewHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static GraphViewHelper newInstance(@NotNull FunctionViewDef functionViewDef,
|
public static GraphViewHelper newInstance(@NotNull PlotViewDef plotViewDef,
|
||||||
@NotNull List<PlotFunction> plotFunctions) {
|
@NotNull List<PlotFunction> plotFunctions) {
|
||||||
final GraphViewHelper result = new GraphViewHelper();
|
final GraphViewHelper result = new GraphViewHelper();
|
||||||
|
|
||||||
result.functionViewDef = functionViewDef;
|
result.plotViewDef = plotViewDef;
|
||||||
result.functionPlotDefs = Collections.unmodifiableList(plotFunctions);
|
result.plotFunctions = Collections.unmodifiableList(plotFunctions);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -41,19 +41,19 @@ public class GraphViewHelper {
|
|||||||
public GraphViewHelper copy(@NotNull List<PlotFunction> plotFunctions) {
|
public GraphViewHelper copy(@NotNull List<PlotFunction> plotFunctions) {
|
||||||
final GraphViewHelper result = new GraphViewHelper();
|
final GraphViewHelper result = new GraphViewHelper();
|
||||||
|
|
||||||
result.functionViewDef = functionViewDef;
|
result.plotViewDef = plotViewDef;
|
||||||
result.functionPlotDefs = Collections.unmodifiableList(plotFunctions);
|
result.plotFunctions = Collections.unmodifiableList(plotFunctions);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<PlotFunction> getFunctionPlotDefs() {
|
public List<PlotFunction> getPlotFunctions() {
|
||||||
return functionPlotDefs;
|
return plotFunctions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public FunctionViewDef getFunctionViewDef() {
|
public PlotViewDef getPlotViewDef() {
|
||||||
return functionViewDef;
|
return plotViewDef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 1/18/13
|
||||||
|
* Time: 8:32 PM
|
||||||
|
*/
|
||||||
|
public class GraphsData {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final GraphView graphView;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private List<GraphData> graphs;
|
||||||
|
|
||||||
|
private float lastXMin;
|
||||||
|
private float lastXMax;
|
||||||
|
|
||||||
|
private float lastYMin;
|
||||||
|
|
||||||
|
private float lastYMax;
|
||||||
|
|
||||||
|
public GraphsData(@NotNull GraphView graphView) {
|
||||||
|
this.graphView = graphView;
|
||||||
|
graphs = new ArrayList<GraphData>(graphView.getPlotFunctions().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
for (GraphData graph : graphs) {
|
||||||
|
graph.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (graphView.getPlotFunctions().size() > graphs.size()) {
|
||||||
|
graphs.add(GraphData.newEmptyInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
lastYMin = 0;
|
||||||
|
lastYMax = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public List<GraphData> getGraphs() {
|
||||||
|
return graphs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLastXMin() {
|
||||||
|
return lastXMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLastXMax() {
|
||||||
|
return lastXMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLastYMin() {
|
||||||
|
return lastYMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLastYMax() {
|
||||||
|
return lastYMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkBoundaries(float graphHeight, float yMin, float yMax) {
|
||||||
|
if (yMin < lastYMin || yMax > lastYMax) {
|
||||||
|
float halfGraphHeight = graphHeight / 2;
|
||||||
|
clear();
|
||||||
|
lastYMin = yMin - halfGraphHeight;
|
||||||
|
lastYMax = yMax + halfGraphHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastXMin(float lastXMin) {
|
||||||
|
this.lastXMin = lastXMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastXMax(float lastXMax) {
|
||||||
|
this.lastXMax = lastXMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public GraphData get(int i) {
|
||||||
|
return this.graphs.get(i);
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
package org.solovyev.android.calculator.plot;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import org.achartengine.GraphicalView;
|
|
||||||
import org.achartengine.chart.AbstractChart;
|
|
||||||
import org.solovyev.android.calculator.Locator;
|
|
||||||
|
|
||||||
public class MyGraphicalView extends GraphicalView {
|
|
||||||
|
|
||||||
private static final String TAG = MyGraphicalView.class.getSimpleName();
|
|
||||||
|
|
||||||
public MyGraphicalView(Context context, AbstractChart chart) {
|
|
||||||
super(context, chart);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
try {
|
|
||||||
super.onDraw(canvas);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Locator.getInstance().getLogger().error(TAG, e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,258 +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 org.achartengine.model.XYSeries;
|
|
||||||
import org.achartengine.util.MathHelper;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 12/5/11
|
|
||||||
* Time: 8:43 PM
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BEST SOLUTION IS TO MODIFY LIBRARY CLASS
|
|
||||||
* NOTE: this class is a copy of XYSeries with some modifications:
|
|
||||||
* 1. Possibility to insert point in th emiddle og the range
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class MyXYSeries extends XYSeries {
|
|
||||||
/**
|
|
||||||
* The series title.
|
|
||||||
*/
|
|
||||||
private String mTitle;
|
|
||||||
/**
|
|
||||||
* A list to contain the values for the X axis.
|
|
||||||
*/
|
|
||||||
private List<Double> mX = new ArrayList<Double>();
|
|
||||||
/**
|
|
||||||
* A list to contain the values for the Y axis.
|
|
||||||
*/
|
|
||||||
private List<Double> mY = new ArrayList<Double>();
|
|
||||||
/**
|
|
||||||
* The minimum value for the X axis.
|
|
||||||
*/
|
|
||||||
private double mMinX = MathHelper.NULL_VALUE;
|
|
||||||
/**
|
|
||||||
* The maximum value for the X axis.
|
|
||||||
*/
|
|
||||||
private double mMaxX = -MathHelper.NULL_VALUE;
|
|
||||||
/**
|
|
||||||
* The minimum value for the Y axis.
|
|
||||||
*/
|
|
||||||
private double mMinY = MathHelper.NULL_VALUE;
|
|
||||||
/**
|
|
||||||
* The maximum value for the Y axis.
|
|
||||||
*/
|
|
||||||
private double mMaxY = -MathHelper.NULL_VALUE;
|
|
||||||
/**
|
|
||||||
* The scale number for this series.
|
|
||||||
*/
|
|
||||||
private int mScaleNumber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a new XY series.
|
|
||||||
*
|
|
||||||
* @param title the series title.
|
|
||||||
*/
|
|
||||||
public MyXYSeries(String title) {
|
|
||||||
this(title, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MyXYSeries(String title, int initialCapacity) {
|
|
||||||
super(title, 0);
|
|
||||||
|
|
||||||
this.mX = new ArrayList<Double>(initialCapacity);
|
|
||||||
this.mY = new ArrayList<Double>(initialCapacity);
|
|
||||||
|
|
||||||
mTitle = title;
|
|
||||||
mScaleNumber = 0;
|
|
||||||
|
|
||||||
initRange();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getScaleNumber() {
|
|
||||||
return mScaleNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the range for both axes.
|
|
||||||
*/
|
|
||||||
private void initRange() {
|
|
||||||
mMinX = MathHelper.NULL_VALUE;
|
|
||||||
mMaxX = -MathHelper.NULL_VALUE;
|
|
||||||
mMinY = MathHelper.NULL_VALUE;
|
|
||||||
mMaxY = -MathHelper.NULL_VALUE;
|
|
||||||
int length = getItemCount();
|
|
||||||
for (int k = 0; k < length; k++) {
|
|
||||||
double x = getX(k);
|
|
||||||
double y = getY(k);
|
|
||||||
updateRange(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the range on both axes.
|
|
||||||
*
|
|
||||||
* @param x the new x value
|
|
||||||
* @param y the new y value
|
|
||||||
*/
|
|
||||||
private void updateRange(double x, double y) {
|
|
||||||
mMinX = Math.min(mMinX, x);
|
|
||||||
mMaxX = Math.max(mMaxX, x);
|
|
||||||
mMinY = Math.min(mMinY, y);
|
|
||||||
mMaxY = Math.max(mMaxY, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the series title.
|
|
||||||
*
|
|
||||||
* @return the series title
|
|
||||||
*/
|
|
||||||
public String getTitle() {
|
|
||||||
return mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the series title.
|
|
||||||
*
|
|
||||||
* @param title the series title
|
|
||||||
*/
|
|
||||||
public void setTitle(String title) {
|
|
||||||
mTitle = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new value to the series.
|
|
||||||
*
|
|
||||||
* @param x the value for the X axis
|
|
||||||
* @param y the value for the Y axis
|
|
||||||
*/
|
|
||||||
public void add(double x, double y) {
|
|
||||||
boolean added = false;
|
|
||||||
for (int i = 0; i < mX.size(); i++ ) {
|
|
||||||
if ( mX.get(i) > x ) {
|
|
||||||
mX.add(i, x);
|
|
||||||
mY.add(i, y);
|
|
||||||
added = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !added ) {
|
|
||||||
mX.add(x);
|
|
||||||
mY.add(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateRange(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean needToAdd(double density, double x) {
|
|
||||||
boolean result = true;
|
|
||||||
|
|
||||||
for (Double x1 : mX) {
|
|
||||||
if (Math.abs(x - x1) < density) {
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes an existing value from the series.
|
|
||||||
*
|
|
||||||
* @param index the index in the series of the value to remove
|
|
||||||
*/
|
|
||||||
public void remove(int index) {
|
|
||||||
double removedX = mX.remove(index);
|
|
||||||
double removedY = mY.remove(index);
|
|
||||||
if (removedX == mMinX || removedX == mMaxX || removedY == mMinY || removedY == mMaxY) {
|
|
||||||
initRange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all the existing values from the series.
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
mX.clear();
|
|
||||||
mY.clear();
|
|
||||||
initRange();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the X axis value at the specified index.
|
|
||||||
*
|
|
||||||
* @param index the index
|
|
||||||
* @return the X value
|
|
||||||
*/
|
|
||||||
public double getX(int index) {
|
|
||||||
return mX.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Y axis value at the specified index.
|
|
||||||
*
|
|
||||||
* @param index the index
|
|
||||||
* @return the Y value
|
|
||||||
*/
|
|
||||||
public double getY(int index) {
|
|
||||||
return mY.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the series item count.
|
|
||||||
*
|
|
||||||
* @return the series item count
|
|
||||||
*/
|
|
||||||
public int getItemCount() {
|
|
||||||
return mX == null ? 0 : mX.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the minimum value on the X axis.
|
|
||||||
*
|
|
||||||
* @return the X axis minimum value
|
|
||||||
*/
|
|
||||||
public double getMinX() {
|
|
||||||
return mMinX;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the minimum value on the Y axis.
|
|
||||||
*
|
|
||||||
* @return the Y axis minimum value
|
|
||||||
*/
|
|
||||||
public double getMinY() {
|
|
||||||
return mMinY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximum value on the X axis.
|
|
||||||
*
|
|
||||||
* @return the X axis maximum value
|
|
||||||
*/
|
|
||||||
public double getMaxX() {
|
|
||||||
return mMaxX;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximum value on the Y axis.
|
|
||||||
*
|
|
||||||
* @return the Y axis maximum value
|
|
||||||
*/
|
|
||||||
public double getMaxY() {
|
|
||||||
return mMaxY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* Date: 1/5/13
|
* Date: 1/5/13
|
||||||
* Time: 9:11 PM
|
* Time: 9:11 PM
|
||||||
*/
|
*/
|
||||||
public class FunctionViewDef {
|
public class PlotViewDef {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
@ -38,10 +38,10 @@ public class FunctionViewDef {
|
|||||||
|
|
||||||
private int backgroundColor = DEFAULT_BACKGROUND_COLOR;
|
private int backgroundColor = DEFAULT_BACKGROUND_COLOR;
|
||||||
|
|
||||||
private FunctionViewDef() {
|
private PlotViewDef() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private FunctionViewDef(int axisColor, int axisLabelColor, int gridColor, int backgroundColor) {
|
private PlotViewDef(int axisColor, int axisLabelColor, int gridColor, int backgroundColor) {
|
||||||
this.axisColor = axisColor;
|
this.axisColor = axisColor;
|
||||||
this.axisLabelsColor = axisLabelColor;
|
this.axisLabelsColor = axisLabelColor;
|
||||||
this.gridColor = gridColor;
|
this.gridColor = gridColor;
|
||||||
@ -49,13 +49,13 @@ public class FunctionViewDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static FunctionViewDef newDefaultInstance() {
|
public static PlotViewDef newDefaultInstance() {
|
||||||
return new FunctionViewDef();
|
return new PlotViewDef();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static FunctionViewDef newInstance(int axisColor, int axisLabelColor, int gridColor, int backgroundColor) {
|
public static PlotViewDef newInstance(int axisColor, int axisLabelColor, int gridColor, int backgroundColor) {
|
||||||
return new FunctionViewDef(axisColor, axisLabelColor, gridColor, backgroundColor);
|
return new PlotViewDef(axisColor, axisLabelColor, gridColor, backgroundColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getAxisColor() {
|
public int getAxisColor() {
|
@ -1,47 +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.function.Constant;
|
|
||||||
import junit.framework.Assert;
|
|
||||||
import org.achartengine.model.XYSeries;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 12/5/11
|
|
||||||
* Time: 9:07 PM
|
|
||||||
*/
|
|
||||||
public class PlotUtilsTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddXY() throws Exception {
|
|
||||||
MyXYSeries series = new MyXYSeries("test_01", 100);
|
|
||||||
PlotUtils.addXY(-10, 10, Expression.valueOf("asin(t)"), new Constant("t"), series, new MyXYSeries("test_01_imag"), false, 200);
|
|
||||||
testAscSeries(series);
|
|
||||||
PlotUtils.addXY(-1, 1, Expression.valueOf("asin(t)"), new Constant("t"), series, new MyXYSeries("test_01_imag"), true, 200);
|
|
||||||
testAscSeries(series);
|
|
||||||
|
|
||||||
series = new MyXYSeries("test_02", 1000);
|
|
||||||
PlotUtils.addXY(-10, 10, Expression.valueOf("1/t"), new Constant("t"), series, new MyXYSeries("test_01_imag"), false, 1000);
|
|
||||||
testAscSeries(series);
|
|
||||||
PlotUtils.addXY(-1, 1, Expression.valueOf("1/t"), new Constant("t"), series, new MyXYSeries("test_01_imag"), true, 1000);
|
|
||||||
testAscSeries(series);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAscSeries(@NotNull XYSeries series) {
|
|
||||||
for ( int i = 0; i < series.getItemCount(); i++ ) {
|
|
||||||
if (i > 1) {
|
|
||||||
Assert.assertTrue(series.getX(i - 1) + " > " +series.getX(i) + " at " + i + " of " + series.getItemCount(), series.getX(i - 1) <= series.getX(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user