From bbdec030fe50de2b5bd0e72bf62394b1990c7bc6 Mon Sep 17 00:00:00 2001 From: Sergey Solovyev Date: Fri, 18 Jan 2013 23:04:45 +0400 Subject: [PATCH] new plotter --- .../CalculatorActivityLauncher.java | 2 - .../plot/AbstractGraphCalculator.java | 101 +++++ .../calculator/plot/ArityPlotFunction.java | 46 -- .../plot/CalculatorGraph2dView.java | 422 ++++++------------ .../plot/CalculatorPlotFragment.java | 10 +- .../calculator/plot/Graph2dDimensions.java | 165 +++++++ .../android/calculator/plot/Graph3dView.java | 25 +- .../calculator/plot/GraphCalculator.java | 18 + .../calculator/plot/GraphCalculatorImpl.java | 100 +++++ .../android/calculator/plot/GraphData.java | 56 +-- .../android/calculator/plot/GraphView.java | 9 +- .../calculator/plot/GraphViewHelper.java | 22 +- .../android/calculator/plot/GraphsData.java | 88 ++++ .../calculator/plot/MyGraphicalView.java | 25 -- .../android/calculator/plot/MyXYSeries.java | 258 ----------- ...{FunctionViewDef.java => PlotViewDef.java} | 14 +- .../calculator/plot/PlotUtilsTest.java | 47 -- 17 files changed, 664 insertions(+), 744 deletions(-) create mode 100644 android-app/src/main/java/org/solovyev/android/calculator/plot/AbstractGraphCalculator.java delete mode 100644 android-app/src/main/java/org/solovyev/android/calculator/plot/ArityPlotFunction.java create mode 100644 android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dDimensions.java create mode 100644 android-app/src/main/java/org/solovyev/android/calculator/plot/GraphCalculator.java create mode 100644 android-app/src/main/java/org/solovyev/android/calculator/plot/GraphCalculatorImpl.java create mode 100644 android-app/src/main/java/org/solovyev/android/calculator/plot/GraphsData.java delete mode 100644 android-app/src/main/java/org/solovyev/android/calculator/plot/MyGraphicalView.java delete mode 100644 android-app/src/main/java/org/solovyev/android/calculator/plot/MyXYSeries.java rename android-app/src/main/java/org/solovyev/android/calculator/plot/{FunctionViewDef.java => PlotViewDef.java} (76%) delete mode 100644 android-app/src/test/java/org/solovyev/android/calculator/plot/PlotUtilsTest.java diff --git a/android-app/src/main/java/org/solovyev/android/calculator/CalculatorActivityLauncher.java b/android-app/src/main/java/org/solovyev/android/calculator/CalculatorActivityLauncher.java index a2c717de..6c4d6367 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/CalculatorActivityLauncher.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/CalculatorActivityLauncher.java @@ -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); diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/AbstractGraphCalculator.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/AbstractGraphCalculator.java new file mode 100644 index 00000000..3f4b7893 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/AbstractGraphCalculator.java @@ -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); +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/ArityPlotFunction.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/ArityPlotFunction.java deleted file mode 100644 index ede47c20..00000000 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/ArityPlotFunction.java +++ /dev/null @@ -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; - } -} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorGraph2dView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorGraph2dView.java index 112e62cc..a7bf5bbf 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorGraph2dView.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorGraph2dView.java @@ -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 graphs = new ArrayList(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.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.emptyList()); - } + /* + ********************************************************************** + * + * METHODS + * + ********************************************************************** + */ public void setPlotFunctions(@NotNull List plotFunctions) { @@ -163,8 +138,31 @@ public class CalculatorGraph2dView extends View implements GraphView { } this.graphViewHelper = this.graphViewHelper.copy(plotFunctions); - clearAllGraphs(); - invalidate(); + invalidateGraphs(); + } + + @NotNull + @Override + public List 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; - } - if (scroller.computeScrollOffset()) { - final float ratio = getRatio(); - x0 = scroller.getCurrX() * ratio; - y0 = scroller.getCurrY() * ratio; - if (!scroller.isFinished()) { - invalidate(); - } - } - drawGraph(canvas); - } + protected void onDraw(@NotNull Canvas canvas) { + if (!graphViewHelper.getPlotFunctions().isEmpty()) { - // 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; + if (scroller.computeScrollOffset()) { + final float ratio = dimensions.getGraphToViewRatio(); + dimensions.setXY(scroller.getCurrX() * ratio, scroller.getCurrY() * ratio); + if (!scroller.isFinished()) { + invalidate(); } } - 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(); + drawGraph(canvas); + } } 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 functionPlotDefs = graphViewHelper.getFunctionPlotDefs(); + final List 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); } } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotFragment.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotFragment.java index fad8ec6b..1ed75c20 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotFragment.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotFragment.java @@ -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 - * - ********************************************************************** - */ - } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dDimensions.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dDimensions.java new file mode 100644 index 00000000..655580b1 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dDimensions.java @@ -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; + } +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3dView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3dView.java index 644718bf..ce6aaf57 100755 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3dView.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3dView.java @@ -173,8 +173,8 @@ public class Graph3dView extends GLView implements GraphView { } @Override - public void init(@NotNull FunctionViewDef functionViewDef) { - this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.emptyList()); + public void init(@NotNull PlotViewDef plotViewDef) { + this.graphViewHelper = GraphViewHelper.newInstance(plotViewDef, Collections.emptyList()); } @Override @@ -191,7 +191,13 @@ public class Graph3dView extends GLView implements GraphView { isDirty = true; } - @Override + @NotNull + @Override + public List 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)); } } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphCalculator.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphCalculator.java new file mode 100644 index 00000000..847cd314 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphCalculator.java @@ -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); +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphCalculatorImpl.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphCalculatorImpl.java new file mode 100644 index 00000000..b7cd1e3c --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphCalculatorImpl.java @@ -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); + } +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphData.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphData.java index c91115e1..cbbb6018 100755 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphData.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphData.java @@ -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; + int findPositionAfter(float x, float y) { + int position = 0; + while (position < size && xs[position] <= x) { + ++position; } + if (Float.isNaN(y)) { - while (pos < size && ys[pos] != ys[pos]) { - ++pos; + while (position < size && Float.isNaN(ys[position])) { + ++position; } } - // Calculator.log("pos " + pos); - return pos; + + return 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; - } - } - */ - System.arraycopy(d.xs, pos, xs, size, d.size - pos); - System.arraycopy(d.ys, pos, ys, size, d.size - pos); - size += d.size - pos; + 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() { diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphView.java index 48bc640d..8e507626 100755 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphView.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphView.java @@ -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 plotFunctions); + @NotNull + public List 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();*/ diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphViewHelper.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphViewHelper.java index a6daadb6..e2a1ba12 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphViewHelper.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphViewHelper.java @@ -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 functionPlotDefs = Collections.emptyList(); + private List 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 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 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 getFunctionPlotDefs() { - return functionPlotDefs; + public List getPlotFunctions() { + return plotFunctions; } @NotNull - public FunctionViewDef getFunctionViewDef() { - return functionViewDef; + public PlotViewDef getPlotViewDef() { + return plotViewDef; } } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphsData.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphsData.java new file mode 100644 index 00000000..32e06a0d --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphsData.java @@ -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 graphs; + + private float lastXMin; + private float lastXMax; + + private float lastYMin; + + private float lastYMax; + + public GraphsData(@NotNull GraphView graphView) { + this.graphView = graphView; + graphs = new ArrayList(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 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); + } +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/MyGraphicalView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/MyGraphicalView.java deleted file mode 100644 index a259a99b..00000000 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/MyGraphicalView.java +++ /dev/null @@ -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); - } - } -} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/MyXYSeries.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/MyXYSeries.java deleted file mode 100644 index 30f4145a..00000000 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/MyXYSeries.java +++ /dev/null @@ -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 mX = new ArrayList(); - /** - * A list to contain the values for the Y axis. - */ - private List mY = new ArrayList(); - /** - * 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(initialCapacity); - this.mY = new ArrayList(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; - } -} - diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionViewDef.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/PlotViewDef.java similarity index 76% rename from android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionViewDef.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/PlotViewDef.java index 75c8430f..01a7f8d2 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionViewDef.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/PlotViewDef.java @@ -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() { diff --git a/android-app/src/test/java/org/solovyev/android/calculator/plot/PlotUtilsTest.java b/android-app/src/test/java/org/solovyev/android/calculator/plot/PlotUtilsTest.java deleted file mode 100644 index 4ddc77c8..00000000 --- a/android-app/src/test/java/org/solovyev/android/calculator/plot/PlotUtilsTest.java +++ /dev/null @@ -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)); - } - } - } -}