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.widget.TextView;
|
||||
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
||||
import org.achartengine.ChartFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.android.AndroidUtils2;
|
||||
@ -98,7 +97,6 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
|
||||
|
||||
public static void plotGraph(@NotNull final Context context){
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(ChartFactory.TITLE, context.getString(R.string.c_graph));
|
||||
intent.setClass(context, CalculatorPlotActivity.class);
|
||||
AndroidUtils2.addFlags(intent, false, context);
|
||||
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 java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
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
|
||||
private final Matrix matrix = new Matrix();
|
||||
|
||||
@ -66,25 +61,8 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
|
||||
@NotNull
|
||||
private GraphViewHelper graphViewHelper = GraphViewHelper.newDefaultInstance();
|
||||
|
||||
private final GraphData next = GraphData.newEmptyInstance();
|
||||
|
||||
private final GraphData endGraph = GraphData.newEmptyInstance();
|
||||
|
||||
@NotNull
|
||||
private List<GraphData> graphs = new ArrayList<GraphData>(graphViewHelper.getFunctionPlotDefs().size());
|
||||
|
||||
// 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 final GraphsData graphsData = new GraphsData(this);
|
||||
|
||||
private float lastTouchXPxs = NO_TOUCH;
|
||||
private float lastTouchYPxs = NO_TOUCH;
|
||||
@ -101,6 +79,21 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
@NotNull
|
||||
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) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
@ -119,39 +112,21 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
paint.setAntiAlias(false);
|
||||
textPaint.setAntiAlias(true);
|
||||
|
||||
widthPxs = this.getWidth();
|
||||
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;
|
||||
dimensions.setViewDimensions(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXRange(float xMin, float xMax) {
|
||||
this.gWidth = xMax - xMin;
|
||||
this.x0 = xMin + gWidth / 2;
|
||||
this.y0 = 0;
|
||||
public void init(@NotNull PlotViewDef plotViewDef) {
|
||||
this.graphViewHelper = GraphViewHelper.newInstance(plotViewDef, Collections.<PlotFunction>emptyList());
|
||||
}
|
||||
|
||||
private void clearAllGraphs() {
|
||||
for (GraphData graph : graphs) {
|
||||
graph.clear();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
/*
|
||||
**********************************************************************
|
||||
*
|
||||
* METHODS
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
public void setPlotFunctions(@NotNull List<PlotFunction> plotFunctions) {
|
||||
|
||||
@ -163,9 +138,32 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
}
|
||||
|
||||
this.graphViewHelper = this.graphViewHelper.copy(plotFunctions);
|
||||
clearAllGraphs();
|
||||
invalidateGraphs();
|
||||
}
|
||||
|
||||
@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() {
|
||||
}
|
||||
@ -179,149 +177,22 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
}
|
||||
|
||||
protected void onSizeChanged(int w, int h, int ow, int oh) {
|
||||
widthPxs = w;
|
||||
heightPxs = h;
|
||||
clearAllGraphs();
|
||||
dimensions.setViewDimensions(w, h);
|
||||
}
|
||||
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (graphViewHelper.getFunctionPlotDefs().size() == 0) {
|
||||
return;
|
||||
}
|
||||
protected void onDraw(@NotNull Canvas canvas) {
|
||||
if (!graphViewHelper.getPlotFunctions().isEmpty()) {
|
||||
|
||||
if (scroller.computeScrollOffset()) {
|
||||
final float ratio = getRatio();
|
||||
x0 = scroller.getCurrX() * ratio;
|
||||
y0 = scroller.getCurrY() * ratio;
|
||||
final float ratio = dimensions.getGraphToViewRatio();
|
||||
dimensions.setXY(scroller.getCurrX() * ratio, 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
|
||||
/*
|
||||
private float distance(float x1, float y1, float x2, float y2, float x, float y) {
|
||||
float dx = x2 - x1;
|
||||
float dy = y2 - y1;
|
||||
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();
|
||||
endGraph.clear();
|
||||
}
|
||||
|
||||
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) {
|
||||
final float graphHeight = getGraphHeight();
|
||||
mDrawn = true;
|
||||
|
||||
final float xMin = getXMin();
|
||||
final float xMax = getXMax(xMin);
|
||||
final float graphHeight = dimensions.getGraphHeight();
|
||||
|
||||
final float yMin = getYMin(graphHeight);
|
||||
final float yMax = getYMax(graphHeight, yMin);
|
||||
final float xMin = dimensions.getXMin();
|
||||
final float xMax = dimensions.getXMax(xMin);
|
||||
|
||||
if (yMin < lastYMin || yMax > lastYMax) {
|
||||
float halfGraphHeight = graphHeight / 2;
|
||||
lastYMin = yMin - halfGraphHeight;
|
||||
lastYMax = yMax + halfGraphHeight;
|
||||
clearAllGraphs();
|
||||
}
|
||||
final float yMin = dimensions.getYMin(graphHeight);
|
||||
final float yMax = dimensions.getYMax(graphHeight, yMin);
|
||||
final float widthPxs = dimensions.getVWidthPxs();
|
||||
final float heightPxs = dimensions.getVHeightPxs();
|
||||
|
||||
graphsData.checkBoundaries(graphHeight, yMin, yMax);
|
||||
|
||||
// set background
|
||||
canvas.drawColor(graphViewHelper.getFunctionViewDef().getBackgroundColor());
|
||||
canvas.drawColor(graphViewHelper.getPlotViewDef().getBackgroundColor());
|
||||
|
||||
// prepare paint
|
||||
paint.setStrokeWidth(0);
|
||||
paint.setAntiAlias(false);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
final float ratio = getRatio();
|
||||
final float ratio = dimensions.getGraphToViewRatio();
|
||||
|
||||
float x0px = -xMin / ratio;
|
||||
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);
|
||||
|
||||
{
|
||||
// GRID
|
||||
|
||||
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.setTextAlign(Paint.Align.CENTER);
|
||||
|
||||
@ -461,7 +311,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
{
|
||||
// AXIS
|
||||
|
||||
paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor());
|
||||
paint.setColor(graphViewHelper.getPlotViewDef().getAxisColor());
|
||||
canvas.drawLine(x0px, 0, x0px, heightPxs, 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) {
|
||||
|
||||
paint.setColor(graphViewHelper.getFunctionViewDef().getGridColor());
|
||||
paint.setColor(graphViewHelper.getPlotViewDef().getGridColor());
|
||||
paint.setAlpha(100);
|
||||
|
||||
canvas.drawLine(lastTouchXPxs, 0, lastTouchXPxs, heightPxs, 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) + "]";
|
||||
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.preTranslate(-this.x0, -this.y0);
|
||||
matrix.preTranslate(-dimensions.getX0(), -dimensions.getY0());
|
||||
matrix.postScale(1/ratio, -1/ratio);
|
||||
matrix.postTranslate(widthPxs / 2, heightPxs / 2);
|
||||
|
||||
@ -497,16 +347,17 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
{
|
||||
//GRAPH
|
||||
|
||||
final List<PlotFunction> functionPlotDefs = graphViewHelper.getFunctionPlotDefs();
|
||||
final List<PlotFunction> functionPlotDefs = graphViewHelper.getPlotFunctions();
|
||||
|
||||
// create path once
|
||||
final Path path = new Path();
|
||||
|
||||
for (int i = 0; i < functionPlotDefs.size(); 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);
|
||||
|
||||
@ -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
|
||||
|
||||
public float getXMin() {
|
||||
return x0 - gWidth / 2;
|
||||
}
|
||||
|
||||
private float getXMax(float minX) {
|
||||
return minX + gWidth;
|
||||
return dimensions.getXMin();
|
||||
}
|
||||
|
||||
public float getXMax() {
|
||||
return getXMax(getXMin());
|
||||
return dimensions.getXMax();
|
||||
}
|
||||
|
||||
// Y
|
||||
|
||||
@Override
|
||||
public float getYMin() {
|
||||
return getYMin(getGraphHeight());
|
||||
return dimensions.getYMin();
|
||||
}
|
||||
|
||||
|
||||
public float getYMin(float graphHeight) {
|
||||
return y0 - graphHeight / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYMax() {
|
||||
final float graphHeight = getGraphHeight();
|
||||
return getYMax(graphHeight, getYMin(graphHeight));
|
||||
return dimensions.getYMax();
|
||||
}
|
||||
|
||||
public float getYMax(float graphHeight, float yMin) {
|
||||
return yMin + graphHeight;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@Override
|
||||
public void setXRange(float xMin, float xMax) {
|
||||
this.dimensions.setXRange(xMin, xMax);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -647,13 +488,11 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
public void onZoom(boolean zoomIn) {
|
||||
if (zoomIn) {
|
||||
if (canZoomIn()) {
|
||||
gWidth /= 2;
|
||||
invalidateGraphs();
|
||||
dimensions.setGWidth(dimensions.getGWidth() / 2);
|
||||
}
|
||||
} else {
|
||||
if (canZoomOut()) {
|
||||
gWidth *= 2;
|
||||
invalidateGraphs();
|
||||
dimensions.setGWidth(dimensions.getGWidth() * 2);
|
||||
}
|
||||
}
|
||||
zoomController.setZoomInEnabled(canZoomIn());
|
||||
@ -700,7 +539,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
}
|
||||
|
||||
public void onTouchUp(float x, float y) {
|
||||
final float ratio = getRatio();
|
||||
final float ratio = dimensions.getGraphToViewRatio();
|
||||
|
||||
lastTouchXPxs = NO_TOUCH;
|
||||
lastTouchYPxs = NO_TOUCH;
|
||||
@ -715,35 +554,26 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
} else if (asy < asx / 3) {
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!zoomTracker.update(x1, y1, x2, y2)) {
|
||||
return;
|
||||
}
|
||||
float targetGwidth = zoomTracker.value;
|
||||
if (targetGwidth > .25f && targetGwidth < 200) {
|
||||
gWidth = targetGwidth;
|
||||
float targetGWidth = zoomTracker.value;
|
||||
if (targetGWidth > .25f && targetGWidth < 200) {
|
||||
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) {
|
||||
final float scale = gWidth / widthPxs;
|
||||
final float scale = dimensions.getGWidth() / dimensions.getVWidthPxs();
|
||||
float dx = deltaX * scale;
|
||||
float dy = deltaY * scale;
|
||||
final float adx = Math.abs(dx);
|
||||
@ -753,7 +583,7 @@ public class CalculatorGraph2dView extends View implements GraphView {
|
||||
} else if (ady < adx / 3) {
|
||||
dy = 0;
|
||||
}
|
||||
x0 += dx;
|
||||
y0 += dy;
|
||||
|
||||
dimensions.increaseXY(dx, dy);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
|
||||
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.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
|
||||
public void init(@NotNull FunctionViewDef functionViewDef) {
|
||||
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<PlotFunction>emptyList());
|
||||
public void init(@NotNull PlotViewDef plotViewDef) {
|
||||
this.graphViewHelper = GraphViewHelper.newInstance(plotViewDef, Collections.<PlotFunction>emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -191,6 +191,12 @@ public class Graph3dView extends GLView implements GraphView {
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<PlotFunction> getPlotFunctions() {
|
||||
return this.graphViewHelper.getPlotFunctions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXRange(float xMin, float xMax) {
|
||||
//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.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGraphs() {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, int width, int height) {
|
||||
gl.glDisable(GL10.GL_DITHER);
|
||||
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.glShadeModel(useHighQuality3d ? GL10.GL_SMOOTH : GL10.GL_FLAT);
|
||||
@ -248,8 +259,8 @@ public class Graph3dView extends GLView implements GraphView {
|
||||
}
|
||||
if (isDirty) {
|
||||
ensureGraphsSize(gl);
|
||||
for (int i = 0; i < graphViewHelper.getFunctionPlotDefs().size(); i++) {
|
||||
graphs.get(i).update(gl, graphViewHelper.getFunctionPlotDefs().get(i), zoomLevel);
|
||||
for (int i = 0; i < graphViewHelper.getPlotFunctions().size(); i++) {
|
||||
graphs.get(i).update(gl, graphViewHelper.getPlotFunctions().get(i), zoomLevel);
|
||||
|
||||
}
|
||||
isDirty = false;
|
||||
@ -308,7 +319,7 @@ public class Graph3dView extends GLView implements GraphView {
|
||||
}
|
||||
|
||||
private void ensureGraphsSize(@NotNull GL11 gl) {
|
||||
while (graphViewHelper.getFunctionPlotDefs().size() > graphs.size()) {
|
||||
while (graphViewHelper.getPlotFunctions().size() > graphs.size()) {
|
||||
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;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -39,7 +37,7 @@ class GraphData {
|
||||
|
||||
void push(float x, float y) {
|
||||
if (size >= allocatedSize) {
|
||||
makeSpace(size + 1);
|
||||
makeSpaceAtTheEnd(size + 1);
|
||||
}
|
||||
|
||||
xs[size] = x;
|
||||
@ -47,9 +45,9 @@ class GraphData {
|
||||
++size;
|
||||
}
|
||||
|
||||
private void makeSpace(int spaceSize) {
|
||||
private void makeSpaceAtTheEnd(int newSize) {
|
||||
int oldAllocatedSize = allocatedSize;
|
||||
while (spaceSize > allocatedSize) {
|
||||
while (newSize > allocatedSize) {
|
||||
allocatedSize += allocatedSize;
|
||||
}
|
||||
|
||||
@ -63,19 +61,20 @@ class GraphData {
|
||||
}
|
||||
}
|
||||
|
||||
float topX() {
|
||||
|
||||
float getLastX() {
|
||||
return xs[size - 1];
|
||||
}
|
||||
|
||||
float topY() {
|
||||
float getLastY() {
|
||||
return ys[size - 1];
|
||||
}
|
||||
|
||||
float firstX() {
|
||||
float getFirstX() {
|
||||
return xs[0];
|
||||
}
|
||||
|
||||
float firstY() {
|
||||
float getFirstY() {
|
||||
return ys[0];
|
||||
}
|
||||
|
||||
@ -115,36 +114,27 @@ class GraphData {
|
||||
}
|
||||
}
|
||||
|
||||
int findPosAfter(float x, float y) {
|
||||
int pos = 0;
|
||||
while (pos < size && xs[pos] <= x) {
|
||||
++pos;
|
||||
}
|
||||
if (Float.isNaN(y)) {
|
||||
while (pos < size && ys[pos] != ys[pos]) {
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
// Calculator.log("pos " + pos);
|
||||
return pos;
|
||||
int findPositionAfter(float x, float y) {
|
||||
int position = 0;
|
||||
while (position < size && xs[position] <= x) {
|
||||
++position;
|
||||
}
|
||||
|
||||
void append(GraphData d) {
|
||||
makeSpace(size + d.size);
|
||||
int pos = d.findPosAfter(xs[size - 1], ys[size - 1]);
|
||||
/*
|
||||
while (pos < d.size && d.xs[pos] <= last) {
|
||||
++pos;
|
||||
}
|
||||
if (last != last) {
|
||||
while (pos < d.size && d.ys[pos] != d.ys[pos]) {
|
||||
++pos;
|
||||
if (Float.isNaN(y)) {
|
||||
while (position < size && Float.isNaN(ys[position])) {
|
||||
++position;
|
||||
}
|
||||
}
|
||||
*/
|
||||
System.arraycopy(d.xs, pos, xs, size, d.size - pos);
|
||||
System.arraycopy(d.ys, pos, ys, size, d.size - pos);
|
||||
size += d.size - pos;
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
void append(GraphData that) {
|
||||
makeSpaceAtTheEnd(size + that.size);
|
||||
int position = that.findPositionAfter(xs[size - 1], ys[size - 1]);
|
||||
System.arraycopy(that.xs, position, xs, size, that.size - position);
|
||||
System.arraycopy(that.ys, position, ys, size, that.size - position);
|
||||
size += that.size - position;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
@ -1,5 +1,3 @@
|
||||
// Copyright (C) 2009-2010 Mihai Preda
|
||||
|
||||
package org.solovyev.android.calculator.plot;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
@ -10,10 +8,13 @@ import java.util.List;
|
||||
|
||||
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);
|
||||
|
||||
@NotNull
|
||||
public List<PlotFunction> getPlotFunctions();
|
||||
|
||||
public void onPause();
|
||||
public void onResume();
|
||||
|
||||
@ -30,6 +31,8 @@ public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHa
|
||||
|
||||
float getYMax();
|
||||
|
||||
void invalidateGraphs();
|
||||
|
||||
/* void increaseDensity();
|
||||
void decreaseDensity();*/
|
||||
|
||||
|
@ -13,10 +13,10 @@ import java.util.List;
|
||||
public class GraphViewHelper {
|
||||
|
||||
@NotNull
|
||||
private FunctionViewDef functionViewDef = FunctionViewDef.newDefaultInstance();
|
||||
private PlotViewDef plotViewDef = PlotViewDef.newDefaultInstance();
|
||||
|
||||
@NotNull
|
||||
private List<PlotFunction> functionPlotDefs = Collections.emptyList();
|
||||
private List<PlotFunction> plotFunctions = Collections.emptyList();
|
||||
|
||||
private GraphViewHelper() {
|
||||
}
|
||||
@ -27,12 +27,12 @@ public class GraphViewHelper {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static GraphViewHelper newInstance(@NotNull FunctionViewDef functionViewDef,
|
||||
public static GraphViewHelper newInstance(@NotNull PlotViewDef plotViewDef,
|
||||
@NotNull List<PlotFunction> plotFunctions) {
|
||||
final GraphViewHelper result = new GraphViewHelper();
|
||||
|
||||
result.functionViewDef = functionViewDef;
|
||||
result.functionPlotDefs = Collections.unmodifiableList(plotFunctions);
|
||||
result.plotViewDef = plotViewDef;
|
||||
result.plotFunctions = Collections.unmodifiableList(plotFunctions);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -41,19 +41,19 @@ public class GraphViewHelper {
|
||||
public GraphViewHelper copy(@NotNull List<PlotFunction> plotFunctions) {
|
||||
final GraphViewHelper result = new GraphViewHelper();
|
||||
|
||||
result.functionViewDef = functionViewDef;
|
||||
result.functionPlotDefs = Collections.unmodifiableList(plotFunctions);
|
||||
result.plotViewDef = plotViewDef;
|
||||
result.plotFunctions = Collections.unmodifiableList(plotFunctions);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<PlotFunction> getFunctionPlotDefs() {
|
||||
return functionPlotDefs;
|
||||
public List<PlotFunction> getPlotFunctions() {
|
||||
return plotFunctions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FunctionViewDef getFunctionViewDef() {
|
||||
return functionViewDef;
|
||||
public PlotViewDef getPlotViewDef() {
|
||||
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
|
||||
* Time: 9:11 PM
|
||||
*/
|
||||
public class FunctionViewDef {
|
||||
public class PlotViewDef {
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
@ -38,10 +38,10 @@ public class FunctionViewDef {
|
||||
|
||||
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.axisLabelsColor = axisLabelColor;
|
||||
this.gridColor = gridColor;
|
||||
@ -49,13 +49,13 @@ public class FunctionViewDef {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FunctionViewDef newDefaultInstance() {
|
||||
return new FunctionViewDef();
|
||||
public static PlotViewDef newDefaultInstance() {
|
||||
return new PlotViewDef();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FunctionViewDef newInstance(int axisColor, int axisLabelColor, int gridColor, int backgroundColor) {
|
||||
return new FunctionViewDef(axisColor, axisLabelColor, gridColor, backgroundColor);
|
||||
public static PlotViewDef newInstance(int axisColor, int axisLabelColor, int gridColor, int backgroundColor) {
|
||||
return new PlotViewDef(axisColor, axisLabelColor, gridColor, backgroundColor);
|
||||
}
|
||||
|
||||
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