blob: 773c387b7d9650e0eea03697d7b99e7e469de044 [file] [log] [blame]
George Mountecd857b2014-06-19 07:51:08 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.transition;
17
18import com.android.internal.R;
19
20import android.content.Context;
21import android.content.res.TypedArray;
22import android.graphics.Matrix;
23import android.graphics.Path;
24import android.graphics.PathMeasure;
25import android.util.AttributeSet;
George Mountecd857b2014-06-19 07:51:08 -070026import android.util.PathParser;
27
28/**
29 * A PathMotion that takes a Path pattern and applies it to the separation between two points.
30 * The starting point of the Path will be moved to the origin and the end point will be scaled
31 * and rotated so that it matches with the target end point.
32 * <p>This may be used in XML as an element inside a transition.</p>
33 * <pre>
34 * {@code
35 * &lt;changeBounds>
George Mountf9557612014-08-29 11:32:13 -070036 * &lt;patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
37 * &lt;/changeBounds>}
George Mountecd857b2014-06-19 07:51:08 -070038 * </pre>
39 */
George Mountf9557612014-08-29 11:32:13 -070040public class PatternPathMotion extends PathMotion {
George Mountecd857b2014-06-19 07:51:08 -070041
George Mountf9557612014-08-29 11:32:13 -070042 private Path mOriginalPatternPath;
George Mountecd857b2014-06-19 07:51:08 -070043
George Mountf9557612014-08-29 11:32:13 -070044 private final Path mPatternPath = new Path();
George Mountecd857b2014-06-19 07:51:08 -070045
46 private final Matrix mTempMatrix = new Matrix();
47
48 /**
George Mountf9557612014-08-29 11:32:13 -070049 * Constructs a PatternPathMotion with a straight-line pattern.
George Mountecd857b2014-06-19 07:51:08 -070050 */
George Mountf9557612014-08-29 11:32:13 -070051 public PatternPathMotion() {
52 mPatternPath.lineTo(1, 0);
53 mOriginalPatternPath = mPatternPath;
George Mountecd857b2014-06-19 07:51:08 -070054 }
55
George Mountf9557612014-08-29 11:32:13 -070056 public PatternPathMotion(Context context, AttributeSet attrs) {
57 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PatternPathMotion);
George Mountecd857b2014-06-19 07:51:08 -070058 try {
George Mountf9557612014-08-29 11:32:13 -070059 String pathData = a.getString(R.styleable.PatternPathMotion_patternPathData);
George Mountecd857b2014-06-19 07:51:08 -070060 if (pathData == null) {
George Mountf9557612014-08-29 11:32:13 -070061 throw new RuntimeException("pathData must be supplied for patternPathMotion");
George Mountecd857b2014-06-19 07:51:08 -070062 }
63 Path pattern = PathParser.createPathFromPathData(pathData);
George Mountf9557612014-08-29 11:32:13 -070064 setPatternPath(pattern);
George Mountecd857b2014-06-19 07:51:08 -070065 } finally {
66 a.recycle();
67 }
68
69 }
70
71 /**
George Mountf9557612014-08-29 11:32:13 -070072 * Creates a PatternPathMotion with the Path defining a pattern of motion between two
73 * coordinates. The pattern will be translated, rotated, and scaled to fit between the start
74 * and end points. The pattern must not be empty and must have the end point differ from the
75 * start point.
George Mountecd857b2014-06-19 07:51:08 -070076 *
George Mountf9557612014-08-29 11:32:13 -070077 * @param patternPath A Path to be used as a pattern for two-dimensional motion.
George Mountecd857b2014-06-19 07:51:08 -070078 */
George Mountf9557612014-08-29 11:32:13 -070079 public PatternPathMotion(Path patternPath) {
80 setPatternPath(patternPath);
George Mountecd857b2014-06-19 07:51:08 -070081 }
82
83 /**
84 * Returns the Path defining a pattern of motion between two coordinates.
85 * The pattern will be translated, rotated, and scaled to fit between the start and end points.
86 * The pattern must not be empty and must have the end point differ from the start point.
87 *
88 * @return the Path defining a pattern of motion between two coordinates.
George Mountf9557612014-08-29 11:32:13 -070089 * @attr ref android.R.styleable#PatternPathMotion_patternPathData
George Mountecd857b2014-06-19 07:51:08 -070090 */
George Mountf9557612014-08-29 11:32:13 -070091 public Path getPatternPath() {
92 return mOriginalPatternPath;
George Mountecd857b2014-06-19 07:51:08 -070093 }
94
95 /**
96 * Sets the Path defining a pattern of motion between two coordinates.
97 * The pattern will be translated, rotated, and scaled to fit between the start and end points.
98 * The pattern must not be empty and must have the end point differ from the start point.
99 *
George Mountf9557612014-08-29 11:32:13 -0700100 * @param patternPath A Path to be used as a pattern for two-dimensional motion.
101 * @attr ref android.R.styleable#PatternPathMotion_patternPathData
George Mountecd857b2014-06-19 07:51:08 -0700102 */
George Mountf9557612014-08-29 11:32:13 -0700103 public void setPatternPath(Path patternPath) {
104 PathMeasure pathMeasure = new PathMeasure(patternPath, false);
George Mountecd857b2014-06-19 07:51:08 -0700105 float length = pathMeasure.getLength();
106 float[] pos = new float[2];
107 pathMeasure.getPosTan(length, pos, null);
108 float endX = pos[0];
109 float endY = pos[1];
110 pathMeasure.getPosTan(0, pos, null);
111 float startX = pos[0];
112 float startY = pos[1];
113
114 if (startX == endX && startY == endY) {
115 throw new IllegalArgumentException("pattern must not end at the starting point");
116 }
117
118 mTempMatrix.setTranslate(-startX, -startY);
119 float dx = endX - startX;
120 float dy = endY - startY;
Neil Fullere573aa92015-02-11 15:49:47 +0000121 float distance = (float) Math.hypot(dx, dy);
George Mountecd857b2014-06-19 07:51:08 -0700122 float scale = 1 / distance;
123 mTempMatrix.postScale(scale, scale);
124 double angle = Math.atan2(dy, dx);
125 mTempMatrix.postRotate((float) Math.toDegrees(-angle));
George Mountf9557612014-08-29 11:32:13 -0700126 patternPath.transform(mTempMatrix, mPatternPath);
127 mOriginalPatternPath = patternPath;
George Mountecd857b2014-06-19 07:51:08 -0700128 }
129
130 @Override
131 public Path getPath(float startX, float startY, float endX, float endY) {
Neil Fullere573aa92015-02-11 15:49:47 +0000132 double dx = endX - startX;
133 double dy = endY - startY;
134 float length = (float) Math.hypot(dx, dy);
George Mountecd857b2014-06-19 07:51:08 -0700135 double angle = Math.atan2(dy, dx);
136
137 mTempMatrix.setScale(length, length);
138 mTempMatrix.postRotate((float) Math.toDegrees(angle));
139 mTempMatrix.postTranslate(startX, startY);
140 Path path = new Path();
George Mountf9557612014-08-29 11:32:13 -0700141 mPatternPath.transform(mTempMatrix, path);
George Mountecd857b2014-06-19 07:51:08 -0700142 return path;
143 }
144
George Mountecd857b2014-06-19 07:51:08 -0700145}