diff --git a/android-app-core/src/main/java/org/solovyev/android/AndroidUtils2.java b/android-app-core/src/main/java/org/solovyev/android/AndroidUtils2.java index ad7fba62..e4d2a935 100644 --- a/android-app-core/src/main/java/org/solovyev/android/AndroidUtils2.java +++ b/android-app-core/src/main/java/org/solovyev/android/AndroidUtils2.java @@ -6,7 +6,9 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; +import android.os.Build; import android.util.Log; +import android.view.MotionEvent; import org.jetbrains.annotations.NotNull; import java.io.File; @@ -21,6 +23,9 @@ import java.io.IOException; */ public final class AndroidUtils2 { + @NotNull + private static final boolean AT_LEAST_API_5 = Build.VERSION.SDK_INT >= 5; + private AndroidUtils2() { throw new AssertionError(); } @@ -97,4 +102,16 @@ public final class AndroidUtils2 { return null; } + + public static int getPointerCountFromMotionEvent(@NotNull MotionEvent event) { + return AT_LEAST_API_5 ? event.getPointerCount() : 1; + } + + public static float getXFromMotionEvent(@NotNull MotionEvent event, int pointer) { + return AT_LEAST_API_5 ? event.getX(pointer) : 0; + } + + public static float getYFromMotionEvent(@NotNull MotionEvent event, int pointer) { + return AT_LEAST_API_5 ? event.getY(pointer) : 0; + } } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java b/android-app/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java index 4e262f7e..d032b5df 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java @@ -63,7 +63,6 @@ public class CalculatorActivity extends SherlockFragmentActivity implements Shar activityHelper.addTab(this, CalculatorFragmentType.variables, null, R.id.main_second_pane); activityHelper.addTab(this, CalculatorFragmentType.functions, null, R.id.main_second_pane); activityHelper.addTab(this, CalculatorFragmentType.operators, null, R.id.main_second_pane); - activityHelper.addTab(this, CalculatorFragmentType.plotter_functions, null, R.id.main_second_pane); activityHelper.addTab(this, CalculatorPlotActivity.getPlotterFragmentType(), null, R.id.main_second_pane); activityHelper.addTab(this, CalculatorFragmentType.faq, null, R.id.main_second_pane); } else { diff --git a/android-app/src/main/java/org/solovyev/android/calculator/CalculatorFragmentType.java b/android-app/src/main/java/org/solovyev/android/calculator/CalculatorFragmentType.java index 3997624f..612cebf8 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/CalculatorFragmentType.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/CalculatorFragmentType.java @@ -13,7 +13,7 @@ import org.solovyev.android.calculator.math.edit.CalculatorFunctionsFragment; import org.solovyev.android.calculator.math.edit.CalculatorOperatorsFragment; import org.solovyev.android.calculator.math.edit.CalculatorVarsFragment; import org.solovyev.android.calculator.matrix.CalculatorMatrixEditFragment; -import org.solovyev.android.calculator.plot.CalculatorArityPlotFragment; +import org.solovyev.android.calculator.plot.CalculatorPlotFragment; import org.solovyev.android.calculator.plot.CalculatorPlotFunctionSettingsActivity; import org.solovyev.android.calculator.plot.CalculatorPlotFunctionsActivity; @@ -32,7 +32,7 @@ public enum CalculatorFragmentType { variables(CalculatorVarsFragment.class, R.layout.vars_fragment, R.string.c_vars), functions(CalculatorFunctionsFragment.class, R.layout.math_entities_fragment, R.string.c_functions), operators(CalculatorOperatorsFragment.class, R.layout.math_entities_fragment, R.string.c_operators), - plotter(CalculatorArityPlotFragment.class, R.layout.cpp_plot_fragment, R.string.c_graph), + plotter(CalculatorPlotFragment.class, R.layout.cpp_plot_fragment, R.string.c_graph), // todo serso: strings plotter_functions(CalculatorPlotFunctionsActivity.CalculatorPlotFunctionsFragment.class, R.layout.cpp_plot_functions_fragment, R.string.c_graph), diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/AbstractCalculatorPlotFragment.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/AbstractCalculatorPlotFragment.java index 7e2911d8..3582ba34 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/AbstractCalculatorPlotFragment.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/AbstractCalculatorPlotFragment.java @@ -393,6 +393,13 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment public PlotBoundaries() { } + private PlotBoundaries(double xMin, double xMax, double yMin, double yMax) { + this.xMin = xMin; + this.xMax = xMax; + this.yMin = yMin; + this.yMax = yMax; + } + public PlotBoundaries(@NotNull XYMultipleSeriesRenderer renderer) { this.xMin = renderer.getXAxisMin(); this.yMin = renderer.getYAxisMin(); @@ -400,6 +407,11 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment this.yMax = renderer.getYAxisMax(); } + @NotNull + public static PlotBoundaries newInstance(double xMin, double xMax, double yMin, double yMax) { + return new PlotBoundaries(xMin, xMax, yMin, yMax); + } + public double getXMin() { return xMin; } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dViewNew.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorGraph2dView.java similarity index 70% rename from android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dViewNew.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorGraph2dView.java index 90d9da70..c0ae3d41 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dViewNew.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorGraph2dView.java @@ -22,11 +22,30 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class Graph2dViewNew extends View implements GraphView { +public class CalculatorGraph2dView extends View implements GraphView { - // view width and height - private int width; - private int height; + /* + ********************************************************************** + * + * CONSTANTS + * + ********************************************************************** + */ + + private static final float TICKS_COUNT = 15; + public static final int TICK_SIZE_PXS = 3; + + /* + ********************************************************************** + * + * FIELDS + * + ********************************************************************** + */ + + // view width and height in pixels + private int widthPxs; + private int heightPxs; @NotNull private final Matrix matrix = new Matrix(); @@ -52,8 +71,11 @@ public class Graph2dViewNew extends View implements GraphView { @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 graphWidth = 20; private float lastXMin; @@ -76,12 +98,12 @@ public class Graph2dViewNew extends View implements GraphView { @NotNull private Scroller scroller; - public Graph2dViewNew(Context context, AttributeSet attrs) { + public CalculatorGraph2dView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } - public Graph2dViewNew(Context context) { + public CalculatorGraph2dView(Context context) { super(context); init(context); } @@ -94,30 +116,30 @@ public class Graph2dViewNew extends View implements GraphView { paint.setAntiAlias(false); textPaint.setAntiAlias(true); - width = this.getWidth(); - height = this.getHeight(); + widthPxs = this.getWidth(); + heightPxs = this.getHeight(); } - @NotNull + @NotNull public Bitmap captureScreenshot() { - final Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + final Bitmap result = Bitmap.createBitmap(widthPxs, heightPxs, Bitmap.Config.RGB_565); Canvas canvas = new Canvas(result); onDraw(canvas); return result; } - @Override - public void setXRange(float xMin, float xMax) { - this.x0 = xMin + graphWidth / 2; - this.graphWidth = xMax - xMin; - } + @Override + public void setXRange(float xMin, float xMax) { + this.x0 = xMin + graphWidth / 2; + this.graphWidth = xMax - xMin; + } - private void clearAllGraphs() { + private void clearAllGraphs() { for (GraphData graph : graphs) { graph.clear(); } - while ( graphViewHelper.getFunctionPlotDefs().size() > graphs.size() ) { + while (graphViewHelper.getFunctionPlotDefs().size() > graphs.size()) { graphs.add(GraphData.newEmptyInstance()); } } @@ -129,7 +151,7 @@ public class Graph2dViewNew extends View implements GraphView { public void setFunctionPlotDefs(@NotNull List functionPlotDefs) { - for (ArityPlotFunction functionPlotDef: functionPlotDefs) { + for (ArityPlotFunction functionPlotDef : functionPlotDefs) { final int arity = functionPlotDef.getFunction().arity(); if (arity != 0 && arity != 1) { throw new IllegalArgumentException("Function must have arity 0 or 1 for 2d plot!"); @@ -141,25 +163,6 @@ public class Graph2dViewNew extends View implements GraphView { invalidate(); } - public void onVisibilityChanged(boolean visible) { - } - - public void onZoom(boolean zoomIn) { - if (zoomIn) { - if (canZoomIn()) { - graphWidth /= 2; - invalidateGraphs(); - } - } else { - if (canZoomOut()) { - graphWidth *= 2; - invalidateGraphs(); - } - } - zoomController.setZoomInEnabled(canZoomIn()); - zoomController.setZoomOutEnabled(canZoomOut()); - } - public void onResume() { } @@ -172,8 +175,8 @@ public class Graph2dViewNew extends View implements GraphView { } protected void onSizeChanged(int w, int h, int ow, int oh) { - width = w; - height = h; + widthPxs = w; + heightPxs = h; clearAllGraphs(); // points = new float[w+w]; } @@ -183,7 +186,7 @@ public class Graph2dViewNew extends View implements GraphView { return; } if (scroller.computeScrollOffset()) { - final float scale = graphWidth / width; + final float scale = graphWidth / widthPxs; x0 = scroller.getCurrX() * scale; y0 = scroller.getCurrY() * scale; if (!scroller.isFinished()) { @@ -259,7 +262,7 @@ public class Graph2dViewNew extends View implements GraphView { graph.push(xMin, eval(function, xMin)); } - final float scale = width / graphWidth; + final float scale = getRatio(); final float maxStep = 15.8976f / scale; final float minStep = .05f / scale; float ythresh = 1 / scale; @@ -364,20 +367,20 @@ public class Graph2dViewNew extends View implements GraphView { } } - private static final float NTICKS = 15; - - private static float stepFactor(float w) { + private static float getStep(float width) { float f = 1; - while (w / f > NTICKS) { + while (width / f > TICKS_COUNT) { f *= 10; } - while (w / f < NTICKS / 10) { + + while (width / f < TICKS_COUNT / 10) { f /= 10; } - float r = w / f; - if (r < NTICKS / 5) { + + final float r = width / f; + if (r < TICKS_COUNT / 5) { return f / 5; - } else if (r < NTICKS / 2) { + } else if (r < TICKS_COUNT / 2) { return f / 2; } else { return f; @@ -387,13 +390,14 @@ public class Graph2dViewNew extends View implements GraphView { private static StringBuilder b = new StringBuilder(); private static char[] buf = new char[20]; - private static StringBuilder format(final float value) { - int pos = 0; + @NotNull + private static CharSequence formatTick(final float tickValue) { + /*int pos = 0; boolean addDot = false; - final boolean negative = value < 0; + final boolean negative = tickValue < 0; - int absValue = Math.round(Math.abs(value) * 100); + int absValue = Math.round(Math.abs(tickValue) * 100); for (int i = 0; i < 2; ++i) { int digit = absValue % 10; absValue /= 10; @@ -418,17 +422,18 @@ public class Graph2dViewNew extends View implements GraphView { b.setLength(0); b.append(buf, 0, pos); b.reverse(); - return b; + return b;*/ + return Float.toString(tickValue); } - private void drawGraph(Canvas canvas) { + private void drawGraph(@NotNull Canvas canvas) { + final float graphHeight = getGraphHeight(); final float xMin = getXMin(); final float xMax = getXMax(xMin); - float graphHeight = graphWidth * height / width; - float yMin = y0 - graphHeight / 2; - float yMax = yMin + graphHeight; + final float yMin = getYMin(graphHeight); + final float yMax = getYMax(graphHeight, yMin); if (yMin < lastYMin || yMax > lastYMax) { float halfGraphHeight = graphHeight / 2; @@ -445,26 +450,21 @@ public class Graph2dViewNew extends View implements GraphView { paint.setAntiAlias(false); paint.setStyle(Paint.Style.STROKE); - final float scale = width / graphWidth; + final float ratio = getRatio(); - float x0 = -xMin * scale; - boolean drawYAxis = true; - if (x0 < 25) { - x0 = 25; - // drawYAxis = false; - } else if (x0 > width - 3) { - x0 = width - 3; - // drawYAxis = false; - } - float y0 = yMax * scale; - if (y0 < 3) { - y0 = 3; - } else if (y0 > height - 15) { - y0 = height - 15; + float x0px = -xMin * ratio; + if (x0px < 25) { + x0px = 25; + } else if (x0px > widthPxs - 3) { + x0px = widthPxs - 3; } - final float tickSize = 3; - final float y2 = y0 + tickSize; + float y0px = yMax * ratio; + if (y0px < 3) { + y0px = 3; + } else if (y0px > heightPxs - 15) { + y0px = heightPxs - 15; + } { @@ -473,29 +473,41 @@ public class Graph2dViewNew extends View implements GraphView { paint.setPathEffect(new DashPathEffect(new float[]{5, 10}, 0)); paint.setColor(graphViewHelper.getFunctionViewDef().getGridColor()); - float step = stepFactor(graphWidth); - // Calculator.log("width " + gwidth + " step " + step); - float v = ((int) (xMin / step)) * step; textPaint.setColor(graphViewHelper.getFunctionViewDef().getAxisLabelsColor()); textPaint.setTextSize(12); textPaint.setTextAlign(Paint.Align.CENTER); - float stepScale = step * scale; - for (float x = (v - xMin) * scale; x <= width; x += stepScale, v += step) { - canvas.drawLine(x, 0, x, height, paint); - if (!(-.001f < v && v < .001f)) { - StringBuilder b = format(v); - canvas.drawText(b, 0, b.length(), x, y2 + 10, textPaint); + + final float step = getStep(graphWidth); + + // round xMin and init first tick + float tick = ((int) (xMin / step)) * step; + + final float y2 = y0px + TICK_SIZE_PXS; + + float stepPxs = step * ratio; + for (float xPxs = (tick - xMin) * ratio; xPxs <= widthPxs; xPxs += stepPxs, tick += step) { + // draw grid line + canvas.drawLine(xPxs, 0, xPxs, heightPxs, paint); + + if (Math.abs(tick) >= .001f) { + final CharSequence tickLabel = formatTick(tick); + + // draw tick label + canvas.drawText(tickLabel, 0, tickLabel.length(), xPxs, y2 + 10, textPaint); } } - final float x1 = x0 - tickSize; - v = ((int) (yMin / step)) * step; + final float x1 = x0px - TICK_SIZE_PXS; + tick = ((int) (yMin / step)) * step; textPaint.setTextAlign(Paint.Align.RIGHT); - for (float y = height - (v - yMin) * scale; y >= 0; y -= stepScale, v += step) { - canvas.drawLine(0, y, width, y, paint); - if (!(-.001f < v && v < .001f)) { - StringBuilder b = format(v); - canvas.drawText(b, 0, b.length(), x1, y + 4, textPaint); + for (float y = heightPxs - (tick - yMin) * ratio; y >= 0; y -= stepPxs, tick += step) { + canvas.drawLine(0, y, widthPxs, y, paint); + + if (Math.abs(tick) >= .001f) { + final CharSequence tickLabel = formatTick(tick); + + // draw tick label + canvas.drawText(tickLabel, 0, tickLabel.length(), x1, y + 4, textPaint); } } @@ -506,17 +518,15 @@ public class Graph2dViewNew extends View implements GraphView { // AXIS paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor()); - if (drawYAxis) { - canvas.drawLine(x0, 0, x0, height, paint); - } - canvas.drawLine(0, y0, width, y0, paint); + canvas.drawLine(x0px, 0, x0px, heightPxs, paint); + canvas.drawLine(0, y0px, widthPxs, y0px, paint); } matrix.reset(); matrix.preTranslate(-this.x0, -this.y0); - matrix.postScale(scale, -scale); - matrix.postTranslate(width / 2, height / 2); + matrix.postScale(ratio, -ratio); + matrix.postTranslate(widthPxs / 2, heightPxs / 2); paint.setAntiAlias(false); @@ -546,18 +556,78 @@ public class Graph2dViewNew extends View implements GraphView { lastXMin = xMin; } + /* + ********************************************************************** + * + * BOUNDARIES + * + ********************************************************************** + */ + + // X + + public float getXMin() { + return x0 - graphWidth / 2; + } + private float getXMax(float minX) { return minX + graphWidth; } - private float getXMax() { + public float getXMax() { return getXMax(getXMin()); } - private float getXMin() { - return x0 - graphWidth / 2; + // Y + + @Override + public float getYMin() { + return getYMin(getGraphHeight()); } + + public float getYMin(float graphHeight) { + return y0 - graphHeight / 2; + } + + @Override + public float getYMax() { + final float graphHeight = getGraphHeight(); + return getYMax(graphHeight, getYMin(graphHeight)); + } + + public float getYMax(float graphHeight, float yMin) { + return yMin + graphHeight; + } + + private float getGraphHeight() { + return graphWidth * getAspectRatio(); + } + + private float getRatio() { + if (graphWidth != 0) { + return widthPxs / graphWidth; + } else { + return 0; + } + } + + private int getAspectRatio() { + if (widthPxs != 0) { + return heightPxs / widthPxs; + } else { + return 0; + } + } + + /* + ********************************************************************** + * + * ZOOM + * + ********************************************************************** + */ + private boolean canZoomIn() { return true; } @@ -566,15 +636,40 @@ public class Graph2dViewNew extends View implements GraphView { return true; } - private void invalidateGraphs() { - clearAllGraphs(); - lastYMin = lastYMax = 0; - invalidate(); + public void onVisibilityChanged(boolean visible) { } + public void onZoom(boolean zoomIn) { + if (zoomIn) { + if (canZoomIn()) { + graphWidth /= 2; + invalidateGraphs(); + } + } else { + if (canZoomOut()) { + graphWidth *= 2; + invalidateGraphs(); + } + } + zoomController.setZoomInEnabled(canZoomIn()); + zoomController.setZoomOutEnabled(canZoomOut()); + } + + /* + ********************************************************************** + * + * TOUCH + * + ********************************************************************** + */ + @Override - public boolean onTouchEvent(MotionEvent event) { - return touchHandler != null ? touchHandler.onTouchEvent(event) : super.onTouchEvent(event); + public boolean onTouchEvent(@NotNull MotionEvent event) { + boolean handled = touchHandler.handleTouchEvent(event); + if (!handled) { + handled = super.onTouchEvent(event); + } + return handled; } public void onTouchDown(float x, float y) { @@ -598,9 +693,11 @@ public class Graph2dViewNew extends View implements GraphView { } public void onTouchUp(float x, float y) { - final float scale = width / graphWidth; - float sx = -touchHandler.velocityTracker.getXVelocity(); - float sy = touchHandler.velocityTracker.getYVelocity(); + final float ratio = getRatio(); + + float sx = -touchHandler.getXVelocity(); + float sy = touchHandler.getYVelocity(); + final float asx = Math.abs(sx); final float asy = Math.abs(sy); if (asx < asy / 3) { @@ -608,9 +705,7 @@ public class Graph2dViewNew extends View implements GraphView { } else if (asy < asx / 3) { sy = 0; } - scroller.fling(Math.round(x0 * scale), - Math.round(y0 * scale), - Math.round(sx), Math.round(sy), -10000, 10000, -10000, 10000); + 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); invalidate(); } @@ -631,8 +726,14 @@ public class Graph2dViewNew extends View implements GraphView { // Calculator.log("zoom redraw"); } + private void invalidateGraphs() { + clearAllGraphs(); + lastYMin = lastYMax = 0; + invalidate(); + } + private void scroll(float deltaX, float deltaY) { - final float scale = graphWidth / width; + final float scale = graphWidth / widthPxs; float dx = deltaX * scale; float dy = deltaY * scale; final float adx = Math.abs(dx); diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorArityPlotFragment.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotFragment.java similarity index 96% rename from android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorArityPlotFragment.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotFragment.java index de49d5e9..4d4215d6 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorArityPlotFragment.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorPlotFragment.java @@ -19,7 +19,7 @@ import java.util.List; * Date: 12/30/12 * Time: 4:43 PM */ -public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment { +public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment { @Nullable private GraphView graphView; @@ -28,8 +28,7 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment @Override protected PlotBoundaries getPlotBoundaries() { if ( graphView != null ) { - // todo serso: return plot boundaries - return null; + return PlotBoundaries.newInstance(graphView.getXMin(), graphView.getXMax(), graphView.getYMin(), graphView.getYMax()); } else { return null; } @@ -70,7 +69,7 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment if ( plotData.isPlot3d() ) { graphView = new Graph3dView(getActivity()); } else { - graphView = new Graph2dView(getActivity()); + graphView = new CalculatorGraph2dView(getActivity()); } graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor())); diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dView.java deleted file mode 100755 index d818fbe9..00000000 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dView.java +++ /dev/null @@ -1,618 +0,0 @@ -// Copyright (C) 2009-2010 Mihai Preda - -package org.solovyev.android.calculator.plot; - - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.DashPathEffect; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Path; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.widget.Scroller; -import android.widget.ZoomButtonsController; -import org.javia.arity.Function; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class Graph2dView extends View implements GraphView { - - private int width, height; - private Matrix matrix = new Matrix(); - private Paint paint = new Paint(), textPaint = new Paint(), fillPaint = new Paint(); - - @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()); - - private float gwidth = 8; - private float currentX, currentY; - private float lastMinX; - private Scroller scroller; - private float boundMinY, boundMaxY; - protected ZoomButtonsController zoomController = new ZoomButtonsController(this); - private ZoomTracker zoomTracker = new ZoomTracker(); - private TouchHandler touchHandler; - private float lastTouchX, lastTouchY; - - private static final int - COL_ZOOM = 0x40ffffff, - COL_ZOOM_TEXT1 = 0xd0ffffff, - COL_ZOOM_TEXT2 = 0x30ffffff; - - public Graph2dView(Context context, AttributeSet attrs) { - super(context, attrs); - init(context); - } - - public Graph2dView(Context context) { - super(context); - touchHandler = new TouchHandler(this); - init(context); - } - - private void init(Context context) { - zoomController.setOnZoomListener(this); - scroller = new Scroller(context); - paint.setAntiAlias(false); - textPaint.setAntiAlias(true); - } - - @NotNull - public Bitmap captureScreenshot() { - final Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - Canvas canvas = new Canvas(result); - onDraw(canvas); - return result; - } - - @Override - public void setXRange(float xMin, float xMax) { - this.gwidth = xMax - xMin; - this.currentX = xMax - this.gwidth / 2; - - } - - 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()); - } - - public void setFunctionPlotDefs(@NotNull List functionPlotDefs) { - - for (ArityPlotFunction functionPlotDef: functionPlotDefs) { - final int arity = functionPlotDef.getFunction().arity(); - if (arity != 0 && arity != 1) { - throw new IllegalArgumentException("Function must have arity 0 or 1 for 2d plot!"); - } - } - - this.graphViewHelper = this.graphViewHelper.copy(functionPlotDefs); - clearAllGraphs(); - invalidate(); - } - - public void onVisibilityChanged(boolean visible) { - } - - public void onZoom(boolean zoomIn) { - if (zoomIn) { - if (canZoomIn()) { - gwidth /= 2; - invalidateGraphs(); - } - } else { - if (canZoomOut()) { - gwidth *= 2; - invalidateGraphs(); - } - } - zoomController.setZoomInEnabled(canZoomIn()); - zoomController.setZoomOutEnabled(canZoomOut()); - } - - public void onResume() { - } - - public void onPause() { - } - - public void onDetachedFromWindow() { - zoomController.setVisible(false); - super.onDetachedFromWindow(); - } - - protected void onSizeChanged(int w, int h, int ow, int oh) { - width = w; - height = h; - clearAllGraphs(); - // points = new float[w+w]; - } - - protected void onDraw(Canvas canvas) { - if (graphViewHelper.getFunctionPlotDefs().size() == 0) { - return; - } - if (scroller.computeScrollOffset()) { - final float scale = gwidth / width; - currentX = scroller.getCurrX() * scale; - currentY = scroller.getCurrY() * scale; - if (!scroller.isFinished()) { - invalidate(); - } - } - drawGraph(canvas); - } - - private float eval(Function f, float x) { - float v = (float) f.eval(x); - // Calculator.log("eval " + x + "; " + v); - if (v < -10000f) { - return -10000f; - } - if (v > 10000f) { - return 10000f; - } - return v; - } - - // 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 Function function, - float minX, - float maxX, - float minY, - float maxY, - @NotNull GraphData graph) { - if (function.arity() == 0) { - float v = (float) function.eval(); - if (v < -10000f) { - v = -10000f; - } - if (v > 10000f) { - v = 10000f; - } - graph.clear(); - graph.push(minX, v); - graph.push(maxX, v); - return; - } - - final float scale = width / gwidth; - final float maxStep = 15.8976f / scale; - final float minStep = .05f / scale; - float ythresh = 1 / scale; - ythresh = ythresh * ythresh; - // next.clear(); - // endGraph.clear(); - if (!graph.empty()) { - // Calculator.log("last " + lastMinX + " min " + minX); - if (minX >= lastMinX) { - graph.eraseBefore(minX); - } else { - graph.eraseAfter(maxX); - maxX = Math.min(maxX, graph.firstX()); - graph.swap(endGraph); - } - } - if (graph.empty()) { - graph.push(minX, eval(function, minX)); - } - float leftX, leftY; - float rightX = graph.topX(), rightY = graph.topY(); - int nEval = 1; - while (true) { - leftX = rightX; - leftY = rightY; - if (leftX > maxX) { - break; - } - if (next.empty()) { - float x = leftX + maxStep; - next.push(x, eval(function, 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 = eval(function, 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 < minY && rightY > maxY) || (leftY > maxY && rightY < minY))) { - 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) { - - final int size = graph.getSize(); - final float[] xs = graph.getXs(); - final float[] ys = graph.getYs(); - - path.rewind(); - - boolean newCurve = true; - - for (int i = 0; i < size; i++) { - - final float y = ys[i]; - final float x = xs[i]; - - if (y != y) { - newCurve = true; - } else { // !NaN - if (newCurve) { - path.moveTo(x, y); - newCurve = false; - } else { - path.lineTo(x, y); - } - } - } - } - - private static final float NTICKS = 15; - - private static float stepFactor(float w) { - float f = 1; - while (w / f > NTICKS) { - f *= 10; - } - while (w / f < NTICKS / 10) { - f /= 10; - } - float r = w / f; - if (r < NTICKS / 5) { - return f / 5; - } else if (r < NTICKS / 2) { - return f / 2; - } else { - return f; - } - } - - private static StringBuilder b = new StringBuilder(); - private static char[] buf = new char[20]; - - private static StringBuilder format(final float value) { - int pos = 0; - boolean addDot = false; - - final boolean negative = value < 0; - - int absValue = Math.round(Math.abs(value) * 100); - for (int i = 0; i < 2; ++i) { - int digit = absValue % 10; - absValue /= 10; - if (digit != 0 || addDot) { - buf[pos++] = (char) ('0' + digit); - addDot = true; - } - } - if (addDot) { - buf[pos++] = '.'; - } - if (absValue == 0) { - buf[pos++] = '0'; - } - while (absValue != 0) { - buf[pos++] = (char) ('0' + (absValue % 10)); - absValue /= 10; - } - if (negative) { - buf[pos++] = '-'; - } - b.setLength(0); - b.append(buf, 0, pos); - b.reverse(); - return b; - } - - private void drawGraph(Canvas canvas) { - long t1 = System.currentTimeMillis(); - float minX = getXMin(); - float maxX = getXMax(minX); - float ywidth = gwidth * height / width; - float minY = currentY - ywidth / 2; - float maxY = minY + ywidth; - if (minY < boundMinY || maxY > boundMaxY) { - float halfw = ywidth / 2; - boundMinY = minY - halfw; - boundMaxY = maxY + halfw; - clearAllGraphs(); - } - - canvas.drawColor(graphViewHelper.getFunctionViewDef().getBackgroundColor()); - - paint.setStrokeWidth(0); - paint.setAntiAlias(false); - paint.setStyle(Paint.Style.STROKE); - - final float h2 = height / 2f; - final float scale = width / gwidth; - - float x0 = -minX * scale; - boolean drawYAxis = true; - if (x0 < 25) { - x0 = 25; - // drawYAxis = false; - } else if (x0 > width - 3) { - x0 = width - 3; - // drawYAxis = false; - } - float y0 = maxY * scale; - if (y0 < 3) { - y0 = 3; - } else if (y0 > height - 15) { - y0 = height - 15; - } - - final float tickSize = 3; - final float y2 = y0 + tickSize; - - - { - // GRID - - paint.setPathEffect(new DashPathEffect(new float[]{5, 10}, 0)); - paint.setColor(graphViewHelper.getFunctionViewDef().getGridColor()); - - float step = stepFactor(gwidth); - // Calculator.log("width " + gwidth + " step " + step); - float v = ((int) (minX / step)) * step; - textPaint.setColor(graphViewHelper.getFunctionViewDef().getAxisLabelsColor()); - textPaint.setTextSize(12); - textPaint.setTextAlign(Paint.Align.CENTER); - float stepScale = step * scale; - for (float x = (v - minX) * scale; x <= width; x += stepScale, v += step) { - canvas.drawLine(x, 0, x, height, paint); - if (!(-.001f < v && v < .001f)) { - StringBuilder b = format(v); - canvas.drawText(b, 0, b.length(), x, y2 + 10, textPaint); - } - } - - final float x1 = x0 - tickSize; - v = ((int) (minY / step)) * step; - textPaint.setTextAlign(Paint.Align.RIGHT); - for (float y = height - (v - minY) * scale; y >= 0; y -= stepScale, v += step) { - canvas.drawLine(0, y, width, y, paint); - if (!(-.001f < v && v < .001f)) { - StringBuilder b = format(v); - canvas.drawText(b, 0, b.length(), x1, y + 4, textPaint); - } - } - - paint.setPathEffect(null); - } - - { - // AXIS - - paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor()); - if (drawYAxis) { - canvas.drawLine(x0, 0, x0, height, paint); - } - canvas.drawLine(0, y0, width, y0, paint); - } - - - matrix.reset(); - matrix.preTranslate(-currentX, -currentY); - matrix.postScale(scale, -scale); - matrix.postTranslate(width / 2, height / 2); - - paint.setAntiAlias(false); - - { - //GRAPH - - final List functionPlotDefs = graphViewHelper.getFunctionPlotDefs(); - - // create path once - final Path path = new Path(); - - for (int i = 0; i < functionPlotDefs.size(); i++) { - final ArityPlotFunction fpd = functionPlotDefs.get(i); - computeGraph(fpd.getFunction(), minX, maxX, boundMinY, boundMaxY, graphs.get(i)); - - graphToPath(graphs.get(i), path); - - path.transform(matrix); - - AbstractCalculatorPlotFragment.applyToPaint(fpd.getLineDef(), paint); - - canvas.drawPath(path, paint); - } - } - - - lastMinX = minX; - } - - private float getXMax(float minX) { - return minX + gwidth; - } - - private float getXMax() { - return getXMax(getXMin()); - } - - private float getXMin() { - return currentX - gwidth / 2; - } - - private boolean canZoomIn() { - return true; - } - - private boolean canZoomOut() { - return true; - } - - private void invalidateGraphs() { - clearAllGraphs(); - boundMinY = boundMaxY = 0; - invalidate(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - return touchHandler != null ? touchHandler.onTouchEvent(event) : super.onTouchEvent(event); - } - - public void onTouchDown(float x, float y) { - zoomController.setVisible(true); - if (!scroller.isFinished()) { - scroller.abortAnimation(); - } - lastTouchX = x; - lastTouchY = y; - } - - public void onTouchMove(float x, float y) { - float deltaX = x - lastTouchX; - float deltaY = y - lastTouchY; - if (deltaX < -1 || deltaX > 1 || deltaY < -1 || deltaY > 1) { - scroll(-deltaX, deltaY); - lastTouchX = x; - lastTouchY = y; - invalidate(); - } - } - - public void onTouchUp(float x, float y) { - final float scale = width / gwidth; - float sx = -touchHandler.velocityTracker.getXVelocity(); - float sy = touchHandler.velocityTracker.getYVelocity(); - final float asx = Math.abs(sx); - final float asy = Math.abs(sy); - if (asx < asy / 3) { - sx = 0; - } else if (asy < asx / 3) { - sy = 0; - } - scroller.fling(Math.round(currentX * scale), - Math.round(currentY * scale), - Math.round(sx), Math.round(sy), -10000, 10000, -10000, 10000); - invalidate(); - } - - public void onTouchZoomDown(float x1, float y1, float x2, float y2) { - zoomTracker.start(gwidth, 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; - } - // scroll(-zoomTracker.moveX, zoomTracker.moveY); - invalidateGraphs(); - // Calculator.log("zoom redraw"); - } - - private void scroll(float deltaX, float deltaY) { - final float scale = gwidth / width; - float dx = deltaX * scale; - float dy = deltaY * scale; - final float adx = Math.abs(dx); - final float ady = Math.abs(dy); - if (adx < ady / 3) { - dx = 0; - } else if (ady < adx / 3) { - dy = 0; - } - currentX += dx; - currentY += 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 4afcca5d..ce88b6dd 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 @@ -133,8 +133,8 @@ public class Graph3dView extends GLView implements GraphView { } public void onTouchUp(float x, float y) { - float vx = touchHandler.velocityTracker.getXVelocity(); - float vy = touchHandler.velocityTracker.getYVelocity(); + float vx = touchHandler.getXVelocity(); + float vy = touchHandler.getYVelocity(); // Calculator.log("velocity " + vx + ' ' + vy); setRotation(vx / 100, vy / 100); if (shouldRotate()) { @@ -152,7 +152,7 @@ public class Graph3dView extends GLView implements GraphView { @Override public boolean onTouchEvent(MotionEvent event) { - return touchHandler != null ? touchHandler.onTouchEvent(event) : super.onTouchEvent(event); + return touchHandler != null ? touchHandler.handleTouchEvent(event) : super.onTouchEvent(event); } // ---- @@ -195,7 +195,27 @@ public class Graph3dView extends GLView implements GraphView { //To change body of implemented methods use File | Settings | File Templates. } - @Override + @Override + public float getXMin() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public float getXMax() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public float getYMin() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public float getYMax() { + return 0; //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); 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 58b32eb5..d9aab0a7 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 @@ -8,9 +8,7 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHandler.TouchHandlerInterface { - - static final String SCREENSHOT_DIR = "/screenshots"; +public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHandler.TouchHandlerListener { public void init(@NotNull FunctionViewDef functionViewDef); @@ -24,6 +22,14 @@ public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHa void setXRange(float xMin, float xMax); + float getXMin(); + + float getXMax(); + + float getYMin(); + + float getYMax(); + /* void increaseDensity(); void decreaseDensity();*/ diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrap.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrap.java deleted file mode 100755 index 990e5f49..00000000 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrap.java +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2010 Mihai Preda - -package org.solovyev.android.calculator.plot; - -import android.os.Build; -import android.view.MotionEvent; - -class MotionEventWrap { - private static final boolean IS_API_5 = Build.VERSION.SDK_INT >= 5; - - static int getPointerCount(MotionEvent event) { - return IS_API_5 ? MotionEventWrapNew.getPointerCount(event) : 1; - } - - static float getX(MotionEvent event, int idx) { - return IS_API_5 ? MotionEventWrapNew.getX(event, idx) : 0; - } - - static float getY(MotionEvent event, int idx) { - return IS_API_5 ? MotionEventWrapNew.getX(event, idx) : 0; - } -} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrapNew.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrapNew.java deleted file mode 100755 index e8fe7a1b..00000000 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrapNew.java +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2010 Mihai Preda - -package org.solovyev.android.calculator.plot; - -import android.view.MotionEvent; - -class MotionEventWrapNew { - static int getPointerCount(MotionEvent event) { - return event.getPointerCount(); - } - - static float getX(MotionEvent event, int idx) { - return event.getX(idx); - } - - static float getY(MotionEvent event, int idx) { - return event.getY(idx); - } -} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/TouchHandler.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/TouchHandler.java index f5d2865b..ee115b17 100755 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/TouchHandler.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/TouchHandler.java @@ -4,76 +4,95 @@ package org.solovyev.android.calculator.plot; import android.view.MotionEvent; import android.view.VelocityTracker; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.AndroidUtils2; class TouchHandler { - static interface TouchHandlerInterface { + + static interface TouchHandlerListener { void onTouchDown(float x, float y); + void onTouchMove(float x, float y); - void onTouchUp(float x, float y); + + void onTouchUp(float x, float y); + void onTouchZoomDown(float x1, float y1, float x2, float y2); + void onTouchZoomMove(float x1, float y1, float x2, float y2); } - VelocityTracker velocityTracker = VelocityTracker.obtain(); + @NotNull + private final VelocityTracker velocityTracker = VelocityTracker.obtain(); - private boolean isAfterZoom; - private TouchHandlerInterface listener; + private boolean afterZoom; - TouchHandler(TouchHandlerInterface listener) { + @NotNull + private TouchHandlerListener listener; + + TouchHandler(@NotNull TouchHandlerListener listener) { this.listener = listener; } - public boolean onTouchEvent(MotionEvent event) { + public boolean handleTouchEvent(@NotNull MotionEvent event) { // Calculator.log("touch " + event + ' ' + event.getPointerCount() + event.getPointerId(0)); - int fullAction = event.getAction(); - int action = fullAction & MotionEvent.ACTION_MASK; - int pointer = (fullAction & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + final int fullAction = event.getAction(); + final int action = fullAction & MotionEvent.ACTION_MASK; + final int pointer = (fullAction & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + float x = event.getX(); float y = event.getY(); - int nPoints = MotionEventWrap.getPointerCount(event); + + int pointerCount = AndroidUtils2.getPointerCountFromMotionEvent(event); switch (action) { - case MotionEvent.ACTION_DOWN: - isAfterZoom = false; - velocityTracker.clear(); - velocityTracker.addMovement(event); - listener.onTouchDown(x, y); - break; - - case MotionEvent.ACTION_MOVE: - if (nPoints == 1) { - if (isAfterZoom) { - velocityTracker.clear(); - listener.onTouchDown(x, y); - isAfterZoom = false; - } + case MotionEvent.ACTION_DOWN: + afterZoom = false; + velocityTracker.clear(); velocityTracker.addMovement(event); - listener.onTouchMove(x, y); - } else if (nPoints == 2) { - listener.onTouchZoomMove(x, y, MotionEventWrap.getX(event, 1), MotionEventWrap.getY(event, 1)); - } - break; + listener.onTouchDown(x, y); + break; - case MotionEvent.ACTION_UP: - velocityTracker.addMovement(event); - velocityTracker.computeCurrentVelocity(1000); - listener.onTouchUp(x, y); - break; + case MotionEvent.ACTION_MOVE: + if (pointerCount == 1) { + if (afterZoom) { + velocityTracker.clear(); + listener.onTouchDown(x, y); + afterZoom = false; + } + velocityTracker.addMovement(event); + listener.onTouchMove(x, y); + } else if (pointerCount == 2) { + listener.onTouchZoomMove(x, y, AndroidUtils2.getXFromMotionEvent(event, 1), AndroidUtils2.getYFromMotionEvent(event, 1)); + } + break; - case MotionEvent.ACTION_POINTER_DOWN: - if (nPoints == 2) { - listener.onTouchZoomDown(x, y, MotionEventWrap.getX(event, 1), MotionEventWrap.getY(event, 1)); - } - break; + case MotionEvent.ACTION_UP: + velocityTracker.addMovement(event); + velocityTracker.computeCurrentVelocity(1000); + listener.onTouchUp(x, y); + break; - case MotionEvent.ACTION_POINTER_UP: - if (nPoints == 2) { - isAfterZoom = true; - } - break; + case MotionEvent.ACTION_POINTER_DOWN: + if (pointerCount == 2) { + listener.onTouchZoomDown(x, y, AndroidUtils2.getXFromMotionEvent(event, 1), AndroidUtils2.getYFromMotionEvent(event, 1)); + } + break; + + case MotionEvent.ACTION_POINTER_UP: + if (pointerCount == 2) { + afterZoom = true; + } + break; } return true; } + public float getXVelocity() { + return velocityTracker.getXVelocity(); + } + + public float getYVelocity() { + return velocityTracker.getYVelocity(); + } }