Files
frameworks_base/services/java/com/android/server/accessibility/GestureUtils.java
Neil Fuller 33253a4baa Switch from FloatMath -> Math and Math.hypot where possible
The motivation is an API change: FloatMath is going to be
deprecated and/or removed. Performance is not the goal of
this change.

That said...

Math is faster than FloatMath with AOT compilation.

While making the change, occurances of:

{Float}Math.sqrt(x * x + y * y) and
{Float}Math.sqrt({Float}Math.pow(x, 2) + {Float}Math.pow(y, 2))

have been replaced with:

{(float)} Math.hypot(x, y)

Right now there is no runtime intrinsic for hypot so is not faster
in all cases for AOT compilation:

Math.sqrt(x * x + y * y) is faster than Math.hypot(x, y) with
AOT, but all other combinations of FloatMath, use of pow() etc.
are slower than hypot().

hypot() has the advantage of being self documenting and
could be optimized in future. None of the behavior differences
around NaN and rounding appear to be important for the cases
looked at: they all assume results and arguments are in range
and usually the results are cast to float.

Different implementations measured on hammerhead / L:

AOT compiled:

[FloatMath.hypot(x, y)]
benchmark=Hypot_FloatMathHypot} 633.85 ns; σ=0.32 ns @ 3 trials

[FloatMath.sqrt(x*x + y*y)]
benchmark=Hypot_FloatMathSqrtMult} 684.17 ns; σ=4.83 ns @ 3 trials

[FloatMath.sqrt(FloatMath.pow(x, 2) + FloatMath.pow(y, 2))]
benchmark=Hypot_FloatMathSqrtPow} 1270.65 ns; σ=12.20 ns @ 6 trials

[(float) Math.hypot(x, y)]
benchmark=Hypot_MathHypot} 96.80 ns; σ=0.05 ns @ 3 trials

[(float) Math.sqrt(x*x + y*y)]
benchmark=Hypot_MathSqrtMult} 23.97 ns; σ=0.01 ns @ 3 trials

[(float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))]
benchmark=Hypot_MathSqrtPow} 156.19 ns; σ=0.12 ns @ 3 trials

Interpreter:

benchmark=Hypot_FloatMathHypot} 1180.54 ns; σ=5.13 ns @ 3 trials
benchmark=Hypot_FloatMathSqrtMult} 1121.05 ns; σ=3.80 ns @ 3 trials
benchmark=Hypot_FloatMathSqrtPow} 3327.14 ns; σ=7.33 ns @ 3 trials
benchmark=Hypot_MathHypot} 856.57 ns; σ=1.41 ns @ 3 trials
benchmark=Hypot_MathSqrtMult} 1028.92 ns; σ=9.11 ns @ 3 trials
benchmark=Hypot_MathSqrtPow} 2539.47 ns; σ=24.44 ns @ 3 trials

Bug: https://code.google.com/p/android/issues/detail?id=36199
Change-Id: I06c91f682095e627cb547d60d936ef87941be692
2014-10-01 14:04:15 +01:00

101 lines
3.8 KiB
Java

package com.android.server.accessibility;
import android.util.MathUtils;
import android.view.MotionEvent;
/**
* Some helper functions for gesture detection.
*/
final class GestureUtils {
private GestureUtils() {
/* cannot be instantiated */
}
public static boolean isTap(MotionEvent down, MotionEvent up, int tapTimeSlop,
int tapDistanceSlop, int actionIndex) {
return eventsWithinTimeAndDistanceSlop(down, up, tapTimeSlop, tapDistanceSlop, actionIndex);
}
public static boolean isMultiTap(MotionEvent firstUp, MotionEvent secondUp,
int multiTapTimeSlop, int multiTapDistanceSlop, int actionIndex) {
return eventsWithinTimeAndDistanceSlop(firstUp, secondUp, multiTapTimeSlop,
multiTapDistanceSlop, actionIndex);
}
private static boolean eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second,
int timeout, int distance, int actionIndex) {
if (isTimedOut(first, second, timeout)) {
return false;
}
final double deltaMove = computeDistance(first, second, actionIndex);
if (deltaMove >= distance) {
return false;
}
return true;
}
public static double computeDistance(MotionEvent first, MotionEvent second, int pointerIndex) {
return MathUtils.dist(first.getX(pointerIndex), first.getY(pointerIndex),
second.getX(pointerIndex), second.getY(pointerIndex));
}
public static boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
return (deltaTime >= timeout);
}
public static boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
return (first.getPointerIdBits() == second.getPointerIdBits()
&& first.getPointerId(first.getActionIndex())
== second.getPointerId(second.getActionIndex()));
}
/**
* Determines whether a two pointer gesture is a dragging one.
*
* @param event The event with the pointer data.
* @return True if the gesture is a dragging one.
*/
public static boolean isDraggingGesture(float firstPtrDownX, float firstPtrDownY,
float secondPtrDownX, float secondPtrDownY, float firstPtrX, float firstPtrY,
float secondPtrX, float secondPtrY, float maxDraggingAngleCos) {
// Check if the pointers are moving in the same direction.
final float firstDeltaX = firstPtrX - firstPtrDownX;
final float firstDeltaY = firstPtrY - firstPtrDownY;
if (firstDeltaX == 0 && firstDeltaY == 0) {
return true;
}
final float firstMagnitude = (float) Math.hypot(firstDeltaX, firstDeltaY);
final float firstXNormalized =
(firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
final float firstYNormalized =
(firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
final float secondDeltaX = secondPtrX - secondPtrDownX;
final float secondDeltaY = secondPtrY - secondPtrDownY;
if (secondDeltaX == 0 && secondDeltaY == 0) {
return true;
}
final float secondMagnitude = (float) Math.hypot(secondDeltaX, secondDeltaY);
final float secondXNormalized =
(secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
final float secondYNormalized =
(secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
final float angleCos =
firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
if (angleCos < maxDraggingAngleCos) {
return false;
}
return true;
}
}