blob: b5e38b713733a4fcd0fd98f5b8e9882aff0730e7 [file] [log] [blame]
/*
* Copyright (C) 2008-2009 The Android Open Source Project
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package com.android.gesture;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
/**
* A gesture stroke started on a touch down and ended on a touch up.
*/
public class GestureStroke {
public final RectF boundingBox;
public final float length;
public final float[] points;
private final long[] timestamps;
private Path mCachedPath;
/**
* Construct a gesture stroke from a list of gesture points
*
* @param pts
*/
public GestureStroke(ArrayList<GesturePoint> pts) {
float[] tmpPoints = new float[pts.size() * 2];
long[] times = new long[pts.size()];
RectF bx = null;
float len = 0;
int index = 0;
int count = pts.size();
for (int i = 0; i < count; i++) {
GesturePoint p = pts.get(i);
tmpPoints[i * 2] = p.xpos;
tmpPoints[i * 2 + 1] = p.ypos;
times[index] = p.timestamp;
if (bx == null) {
bx = new RectF();
bx.top = p.ypos;
bx.left = p.xpos;
bx.right = p.xpos;
bx.bottom = p.ypos;
len = 0;
} else {
len += Math.sqrt(Math.pow(p.xpos - tmpPoints[(i - 1) * 2], 2)
+ Math.pow(p.ypos - tmpPoints[(i -1 ) * 2 + 1], 2));
bx.union(p.xpos, p.ypos);
}
index++;
}
timestamps = times;
points = tmpPoints;
boundingBox = bx;
length = len;
}
/**
* Draw the gesture with a given canvas and paint
*
* @param canvas
*/
void draw(Canvas canvas, Paint paint) {
if (mCachedPath == null) {
float[] pts = points;
int count = pts.length;
Path path = null;
float mX = 0, mY = 0;
for (int i = 0; i < count; i += 2) {
float x = pts[i];
float y = pts[i + 1];
if (path == null) {
path = new Path();
path.moveTo(x, y);
mX = x;
mY = y;
} else {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= 3 || dy >= 3) {
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
}
mCachedPath = path;
}
canvas.drawPath(mCachedPath, paint);
}
/**
* Convert the stroke to a Path based on the number of points
*
* @param width the width of the bounding box of the target path
* @param height the height of the bounding box of the target path
* @param numSample the number of points needed
* @return the path
*/
public Path toPath(float width, float height, int numSample) {
float[] pts = GestureUtils.temporalSampling(this, numSample);
RectF rect = boundingBox;
float scale = height / rect.height();
Matrix matrix = new Matrix();
matrix.setTranslate(-rect.left, -rect.top);
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(scale, scale);
matrix.postConcat(scaleMatrix);
Matrix translate = new Matrix();
matrix.postConcat(translate);
matrix.mapPoints(pts);
Path path = null;
float mX = 0;
float mY = 0;
int count = pts.length;
for (int i = 0; i < count; i += 2) {
float x = pts[i];
float y = pts[i + 1];
if (path == null) {
path = new Path();
path.moveTo(x, y);
mX = x;
mY = y;
} else {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= GestureOverlay.TOUCH_TOLERANCE || dy >= GestureOverlay.TOUCH_TOLERANCE) {
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
}
return path;
}
/**
* Save the gesture stroke as XML
*
* @param namespace
* @param serializer
* @throws IOException
*/
void toXML(String namespace, XmlSerializer serializer) throws IOException {
serializer.startTag(namespace, GestureConstants.XML_TAG_STROKE);
serializer.text(toString());
serializer.endTag(namespace, GestureConstants.XML_TAG_STROKE);
}
/**
* Create a gesture stroke from a string
*
* @param str
* @return the gesture stroke
*/
public static GestureStroke createFromString(String str) {
ArrayList<GesturePoint> points = new ArrayList<GesturePoint>(
GestureConstants.STROKE_POINT_BUFFER_SIZE);
int endIndex;
int startIndex = 0;
while ((endIndex = str.indexOf(GestureConstants.STRING_STROKE_DELIIMITER, startIndex + 1)) != -1) {
// parse x
String token = str.substring(startIndex, endIndex);
float x = Float.parseFloat(token);
startIndex = endIndex + 1;
// parse y
endIndex = str.indexOf(GestureConstants.STRING_STROKE_DELIIMITER, startIndex + 1);
token = str.substring(startIndex, endIndex);
float y = Float.parseFloat(token);
startIndex = endIndex + 1;
// parse t
endIndex = str.indexOf(GestureConstants.STRING_STROKE_DELIIMITER, startIndex + 1);
token = str.substring(startIndex, endIndex);
long time = Long.parseLong(token);
startIndex = endIndex + 1;
points.add(new GesturePoint(x, y, time));
}
return new GestureStroke(points);
}
/**
* Convert the stroke to string
*/
@Override
public String toString() {
StringBuilder str = new StringBuilder(GestureConstants.STROKE_STRING_BUFFER_SIZE);
float[] pts = points;
long[] times = timestamps;
int count = points.length;
for (int i = 0; i < count; i += 2) {
str.append(points[i] + GestureConstants.STRING_STROKE_DELIIMITER + points[i + 1]
+ GestureConstants.STRING_STROKE_DELIIMITER + times[i / 2]
+ GestureConstants.STRING_STROKE_DELIIMITER);
}
return str.toString();
}
/**
* Invalidate the cached path that is used for rendering the stroke
*/
public void invalidate() {
mCachedPath = null;
}
}