2012-08-20 03:57:34 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
|
|
*
|
2013-01-21 12:52:57 +00:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2012-08-20 03:57:34 +00:00
|
|
|
*
|
2013-01-21 12:52:57 +00:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2012-08-20 03:57:34 +00:00
|
|
|
*
|
2013-01-21 12:52:57 +00:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2012-08-20 03:57:34 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.inputmethod.keyboard.internal;
|
|
|
|
|
|
|
|
import com.android.inputmethod.latin.ResizableIntArray;
|
|
|
|
|
2012-09-27 09:16:16 +00:00
|
|
|
public final class GestureStrokeWithPreviewPoints extends GestureStroke {
|
2012-08-20 03:57:34 +00:00
|
|
|
public static final int PREVIEW_CAPACITY = 256;
|
|
|
|
|
|
|
|
private final ResizableIntArray mPreviewEventTimes = new ResizableIntArray(PREVIEW_CAPACITY);
|
|
|
|
private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
|
|
|
|
private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
|
|
|
|
|
|
|
|
private int mStrokeId;
|
|
|
|
private int mLastPreviewSize;
|
2013-03-07 10:18:13 +00:00
|
|
|
private final HermiteInterpolator mInterpolator = new HermiteInterpolator();
|
|
|
|
private int mLastInterpolatedPreviewIndex;
|
2012-08-20 03:57:34 +00:00
|
|
|
|
2012-09-14 09:10:39 +00:00
|
|
|
private int mLastX;
|
|
|
|
private int mLastY;
|
2013-03-07 10:18:13 +00:00
|
|
|
private double mMinPreviewSamplingDistance;
|
|
|
|
private double mDistanceFromLastSample;
|
2012-09-14 09:10:39 +00:00
|
|
|
|
2013-03-07 10:18:13 +00:00
|
|
|
// TODO: Move these constants to resource.
|
|
|
|
// The minimum trail distance between sample points for preview in keyWidth unit when using
|
|
|
|
// interpolation.
|
2013-05-07 05:27:34 +00:00
|
|
|
private static final float MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH = 0.2f;
|
2013-03-07 10:18:13 +00:00
|
|
|
// The angular threshold to use interpolation in radian. PI/12 is 15 degree.
|
|
|
|
private static final double INTERPOLATION_ANGULAR_THRESHOLD = Math.PI / 12.0d;
|
|
|
|
private static final int MAX_INTERPOLATION_PARTITION = 4;
|
2012-09-14 09:10:39 +00:00
|
|
|
|
2012-10-05 03:12:54 +00:00
|
|
|
public GestureStrokeWithPreviewPoints(final int pointerId, final GestureStrokeParams params) {
|
|
|
|
super(pointerId, params);
|
2012-08-20 03:57:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-09-27 10:01:17 +00:00
|
|
|
protected void reset() {
|
2012-08-20 03:57:34 +00:00
|
|
|
super.reset();
|
|
|
|
mStrokeId++;
|
|
|
|
mLastPreviewSize = 0;
|
2013-03-07 10:18:13 +00:00
|
|
|
mLastInterpolatedPreviewIndex = 0;
|
2012-08-20 03:57:34 +00:00
|
|
|
mPreviewEventTimes.setLength(0);
|
|
|
|
mPreviewXCoordinates.setLength(0);
|
|
|
|
mPreviewYCoordinates.setLength(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getGestureStrokeId() {
|
|
|
|
return mStrokeId;
|
|
|
|
}
|
|
|
|
|
2012-09-14 09:10:39 +00:00
|
|
|
@Override
|
2012-11-22 06:39:28 +00:00
|
|
|
public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
|
|
|
|
super.setKeyboardGeometry(keyWidth, keyboardHeight);
|
2013-05-07 06:51:23 +00:00
|
|
|
mMinPreviewSamplingDistance = keyWidth * MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH;
|
2012-09-14 09:10:39 +00:00
|
|
|
}
|
|
|
|
|
2013-05-07 06:51:23 +00:00
|
|
|
private boolean needsSampling(final int x, final int y) {
|
2013-05-07 05:27:34 +00:00
|
|
|
mDistanceFromLastSample += Math.hypot(x - mLastX, y - mLastY);
|
|
|
|
mLastX = x;
|
|
|
|
mLastY = y;
|
2013-05-07 06:51:23 +00:00
|
|
|
final boolean isDownEvent = (mPreviewEventTimes.getLength() == 0);
|
|
|
|
if (mDistanceFromLastSample >= mMinPreviewSamplingDistance || isDownEvent) {
|
2013-05-07 05:27:34 +00:00
|
|
|
mDistanceFromLastSample = 0.0d;
|
2013-03-07 10:18:13 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2012-09-14 09:10:39 +00:00
|
|
|
}
|
|
|
|
|
2012-08-20 03:57:34 +00:00
|
|
|
@Override
|
2012-11-22 06:39:28 +00:00
|
|
|
public boolean addPointOnKeyboard(final int x, final int y, final int time,
|
|
|
|
final boolean isMajorEvent) {
|
2013-05-07 06:51:23 +00:00
|
|
|
if (needsSampling(x, y)) {
|
2012-09-14 09:10:39 +00:00
|
|
|
mPreviewEventTimes.add(time);
|
|
|
|
mPreviewXCoordinates.add(x);
|
|
|
|
mPreviewYCoordinates.add(y);
|
|
|
|
}
|
2013-03-07 10:18:13 +00:00
|
|
|
return super.addPointOnKeyboard(x, y, time, isMajorEvent);
|
|
|
|
|
2012-08-20 03:57:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void appendPreviewStroke(final ResizableIntArray eventTimes,
|
|
|
|
final ResizableIntArray xCoords, final ResizableIntArray yCoords) {
|
|
|
|
final int length = mPreviewEventTimes.getLength() - mLastPreviewSize;
|
|
|
|
if (length <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
eventTimes.append(mPreviewEventTimes, mLastPreviewSize, length);
|
|
|
|
xCoords.append(mPreviewXCoordinates, mLastPreviewSize, length);
|
|
|
|
yCoords.append(mPreviewYCoordinates, mLastPreviewSize, length);
|
|
|
|
mLastPreviewSize = mPreviewEventTimes.getLength();
|
|
|
|
}
|
2013-03-07 10:18:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate interpolated points between the last interpolated point and the end of the trail.
|
|
|
|
* And return the start index of the last interpolated segment of input arrays because it
|
|
|
|
* may need to recalculate the interpolated points in the segment if further segments are
|
|
|
|
* added to this stroke.
|
|
|
|
*
|
|
|
|
* @param lastInterpolatedIndex the start index of the last interpolated segment of
|
|
|
|
* <code>eventTimes</code>, <code>xCoords</code>, and <code>yCoords</code>.
|
|
|
|
* @param eventTimes the event time array of gesture preview trail to be drawn.
|
|
|
|
* @param xCoords the x-coordinates array of gesture preview trail to be drawn.
|
|
|
|
* @param yCoords the y-coordinates array of gesture preview trail to be drawn.
|
|
|
|
* @return the start index of the last interpolated segment of input arrays.
|
|
|
|
*/
|
|
|
|
public int interpolateStrokeAndReturnStartIndexOfLastSegment(final int lastInterpolatedIndex,
|
|
|
|
final ResizableIntArray eventTimes, final ResizableIntArray xCoords,
|
|
|
|
final ResizableIntArray yCoords) {
|
|
|
|
final int size = mPreviewEventTimes.getLength();
|
|
|
|
final int[] pt = mPreviewEventTimes.getPrimitiveArray();
|
|
|
|
final int[] px = mPreviewXCoordinates.getPrimitiveArray();
|
|
|
|
final int[] py = mPreviewYCoordinates.getPrimitiveArray();
|
|
|
|
mInterpolator.reset(px, py, 0, size);
|
|
|
|
// The last segment of gesture stroke needs to be interpolated again because the slope of
|
|
|
|
// the tangent at the last point isn't determined.
|
|
|
|
int lastInterpolatedDrawIndex = lastInterpolatedIndex;
|
|
|
|
int d1 = lastInterpolatedIndex;
|
|
|
|
for (int p2 = mLastInterpolatedPreviewIndex + 1; p2 < size; p2++) {
|
|
|
|
final int p1 = p2 - 1;
|
|
|
|
final int p0 = p1 - 1;
|
|
|
|
final int p3 = p2 + 1;
|
|
|
|
mLastInterpolatedPreviewIndex = p1;
|
|
|
|
lastInterpolatedDrawIndex = d1;
|
|
|
|
mInterpolator.setInterval(p0, p1, p2, p3);
|
|
|
|
final double m1 = Math.atan2(mInterpolator.mSlope1Y, mInterpolator.mSlope1X);
|
|
|
|
final double m2 = Math.atan2(mInterpolator.mSlope2Y, mInterpolator.mSlope2X);
|
|
|
|
final double dm = Math.abs(angularDiff(m2, m1));
|
|
|
|
final int partition = Math.min((int)Math.ceil(dm / INTERPOLATION_ANGULAR_THRESHOLD),
|
|
|
|
MAX_INTERPOLATION_PARTITION);
|
|
|
|
final int t1 = eventTimes.get(d1);
|
|
|
|
final int dt = pt[p2] - pt[p1];
|
|
|
|
d1++;
|
|
|
|
for (int i = 1; i < partition; i++) {
|
|
|
|
final float t = i / (float)partition;
|
|
|
|
mInterpolator.interpolate(t);
|
|
|
|
eventTimes.add(d1, (int)(dt * t) + t1);
|
|
|
|
xCoords.add(d1, (int)mInterpolator.mInterpolatedX);
|
|
|
|
yCoords.add(d1, (int)mInterpolator.mInterpolatedY);
|
|
|
|
d1++;
|
|
|
|
}
|
|
|
|
eventTimes.add(d1, pt[p2]);
|
|
|
|
xCoords.add(d1, px[p2]);
|
|
|
|
yCoords.add(d1, py[p2]);
|
|
|
|
}
|
|
|
|
return lastInterpolatedDrawIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final double TWO_PI = Math.PI * 2.0d;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate the angular of rotation from <code>a0</code> to <code>a1</code>.
|
|
|
|
*
|
|
|
|
* @param a1 the angular to which the rotation ends.
|
|
|
|
* @param a0 the angular from which the rotation starts.
|
|
|
|
* @return the angular rotation value from a0 to a1, normalized to [-PI, +PI].
|
|
|
|
*/
|
|
|
|
private static double angularDiff(final double a1, final double a0) {
|
|
|
|
double deltaAngle = a1 - a0;
|
|
|
|
while (deltaAngle > Math.PI) {
|
|
|
|
deltaAngle -= TWO_PI;
|
|
|
|
}
|
|
|
|
while (deltaAngle < -Math.PI) {
|
|
|
|
deltaAngle += TWO_PI;
|
|
|
|
}
|
|
|
|
return deltaAngle;
|
|
|
|
}
|
2012-08-20 03:57:34 +00:00
|
|
|
}
|