1ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount/* 2ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * Copyright (C) 2014 The Android Open Source Project 3ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * 4ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * Licensed under the Apache License, Version 2.0 (the "License"); 5ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * you may not use this file except in compliance with the License. 6ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * You may obtain a copy of the License at 7ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * 8ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * http://www.apache.org/licenses/LICENSE-2.0 9ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * 10ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * Unless required by applicable law or agreed to in writing, software 11ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * distributed under the License is distributed on an "AS IS" BASIS, 12ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * See the License for the specific language governing permissions and 14ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * limitations under the License. 15ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount */ 16ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountpackage android.transition; 17ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 18ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport com.android.internal.R; 19ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 20ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.content.Context; 21ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.content.res.TypedArray; 22ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.Matrix; 23ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.Path; 24ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.PathMeasure; 25ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.AttributeSet; 26ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.PathParser; 27ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 28ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount/** 29ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * A PathMotion that takes a Path pattern and applies it to the separation between two points. 30ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * The starting point of the Path will be moved to the origin and the end point will be scaled 31ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * and rotated so that it matches with the target end point. 32ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * <p>This may be used in XML as an element inside a transition.</p> 3371fbb81b14958b80fe55738607740c6630e4e9daNeil Fuller * <pre>{@code 3471fbb81b14958b80fe55738607740c6630e4e9daNeil Fuller * <changeBounds> 3571fbb81b14958b80fe55738607740c6630e4e9daNeil Fuller * <patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/> 3671fbb81b14958b80fe55738607740c6630e4e9daNeil Fuller * </changeBounds>} 37ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * </pre> 38ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount */ 39f9557619a7643c971e64e5b35583476202e77b7bGeorge Mountpublic class PatternPathMotion extends PathMotion { 40ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 41f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount private Path mOriginalPatternPath; 42ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 43f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount private final Path mPatternPath = new Path(); 44ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 45ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount private final Matrix mTempMatrix = new Matrix(); 46ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 47ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount /** 48f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * Constructs a PatternPathMotion with a straight-line pattern. 49ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount */ 50f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount public PatternPathMotion() { 51f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount mPatternPath.lineTo(1, 0); 52f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount mOriginalPatternPath = mPatternPath; 53ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 54ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 55f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount public PatternPathMotion(Context context, AttributeSet attrs) { 56f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PatternPathMotion); 57ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount try { 58f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount String pathData = a.getString(R.styleable.PatternPathMotion_patternPathData); 59ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount if (pathData == null) { 60f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount throw new RuntimeException("pathData must be supplied for patternPathMotion"); 61ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 62ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount Path pattern = PathParser.createPathFromPathData(pathData); 63f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount setPatternPath(pattern); 64ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } finally { 65ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount a.recycle(); 66ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 67ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 68ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 69ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 70ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount /** 71f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * Creates a PatternPathMotion with the Path defining a pattern of motion between two 72f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * coordinates. The pattern will be translated, rotated, and scaled to fit between the start 73f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * and end points. The pattern must not be empty and must have the end point differ from the 74f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * start point. 75ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * 76f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * @param patternPath A Path to be used as a pattern for two-dimensional motion. 77ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount */ 78f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount public PatternPathMotion(Path patternPath) { 79f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount setPatternPath(patternPath); 80ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 81ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 82ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount /** 83ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * Returns the Path defining a pattern of motion between two coordinates. 84ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * The pattern will be translated, rotated, and scaled to fit between the start and end points. 85ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * The pattern must not be empty and must have the end point differ from the start point. 86ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * 87ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * @return the Path defining a pattern of motion between two coordinates. 88f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * @attr ref android.R.styleable#PatternPathMotion_patternPathData 89ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount */ 90f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount public Path getPatternPath() { 91f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount return mOriginalPatternPath; 92ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 93ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 94ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount /** 95ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * Sets the Path defining a pattern of motion between two coordinates. 96ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * The pattern will be translated, rotated, and scaled to fit between the start and end points. 97ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * The pattern must not be empty and must have the end point differ from the start point. 98ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount * 99f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * @param patternPath A Path to be used as a pattern for two-dimensional motion. 100f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount * @attr ref android.R.styleable#PatternPathMotion_patternPathData 101ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount */ 102f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount public void setPatternPath(Path patternPath) { 103f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount PathMeasure pathMeasure = new PathMeasure(patternPath, false); 104ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float length = pathMeasure.getLength(); 105ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float[] pos = new float[2]; 106ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount pathMeasure.getPosTan(length, pos, null); 107ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float endX = pos[0]; 108ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float endY = pos[1]; 109ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount pathMeasure.getPosTan(0, pos, null); 110ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float startX = pos[0]; 111ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float startY = pos[1]; 112ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 113ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount if (startX == endX && startY == endY) { 114ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount throw new IllegalArgumentException("pattern must not end at the starting point"); 115ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 116ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 117ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount mTempMatrix.setTranslate(-startX, -startY); 118ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float dx = endX - startX; 119ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float dy = endY - startY; 120e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller float distance = (float) Math.hypot(dx, dy); 121ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount float scale = 1 / distance; 122ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount mTempMatrix.postScale(scale, scale); 123ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount double angle = Math.atan2(dy, dx); 124ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount mTempMatrix.postRotate((float) Math.toDegrees(-angle)); 125f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount patternPath.transform(mTempMatrix, mPatternPath); 126f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount mOriginalPatternPath = patternPath; 127ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 128ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 129ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount @Override 130ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount public Path getPath(float startX, float startY, float endX, float endY) { 131e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller double dx = endX - startX; 132e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller double dy = endY - startY; 133e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller float length = (float) Math.hypot(dx, dy); 134ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount double angle = Math.atan2(dy, dx); 135ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 136ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount mTempMatrix.setScale(length, length); 137ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount mTempMatrix.postRotate((float) Math.toDegrees(angle)); 138ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount mTempMatrix.postTranslate(startX, startY); 139ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount Path path = new Path(); 140f9557619a7643c971e64e5b35583476202e77b7bGeorge Mount mPatternPath.transform(mTempMatrix, path); 141ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount return path; 142ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount } 143ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount 144ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount} 145