1a1c6015169815e90684917ac215926ae22e61d7aDiego Perez/* 2a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Copyright (C) 2015 The Android Open Source Project 3a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * 4a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Licensed under the Apache License, Version 2.0 (the "License"); 5a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * you may not use this file except in compliance with the License. 6a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * You may obtain a copy of the License at 7a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * 8a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * http://www.apache.org/licenses/LICENSE-2.0 9a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * 10a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Unless required by applicable law or agreed to in writing, software 11a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * distributed under the License is distributed on an "AS IS" BASIS, 12a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * See the License for the specific language governing permissions and 14a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * limitations under the License. 15a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 16a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 17a1c6015169815e90684917ac215926ae22e61d7aDiego Perezpackage com.android.layoutlib.bridge.util; 18a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 19a1c6015169815e90684917ac215926ae22e61d7aDiego Perezimport android.annotation.NonNull; 20a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 21a1c6015169815e90684917ac215926ae22e61d7aDiego Perezimport java.awt.geom.CubicCurve2D; 22a1c6015169815e90684917ac215926ae22e61d7aDiego Perezimport java.awt.geom.PathIterator; 23a1c6015169815e90684917ac215926ae22e61d7aDiego Perezimport java.awt.geom.Point2D; 24a1c6015169815e90684917ac215926ae22e61d7aDiego Perezimport java.awt.geom.QuadCurve2D; 25a1c6015169815e90684917ac215926ae22e61d7aDiego Perezimport java.util.ArrayList; 26a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 27a1c6015169815e90684917ac215926ae22e61d7aDiego Perezimport com.google.android.collect.Lists; 28a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 29a1c6015169815e90684917ac215926ae22e61d7aDiego Perez/** 30a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Class that returns iterators for a given path. These iterators are lightweight and can be reused 31a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * multiple times to iterate over the path. 32a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 33a1c6015169815e90684917ac215926ae22e61d7aDiego Perezpublic class CachedPathIteratorFactory { 34a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /* 35a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * A few conventions used in the code: 36a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Coordinates or coords arrays store segment coordinates. They use the same format as 37a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * PathIterator#currentSegment coordinates array. 38a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * float arrays store always points where the first element is X and the second is Y. 39a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 40a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 41a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // This governs how accurate the approximation of the Path is. 42a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private static final float PRECISION = 0.002f; 43a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 44a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private final int mWindingRule; 45a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private final int[] mTypes; 46a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private final float[][] mCoordinates; 47a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private final float[] mSegmentsLength; 48a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private final float mTotalLength; 49a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 50a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public CachedPathIteratorFactory(@NonNull PathIterator iterator) { 51a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mWindingRule = iterator.getWindingRule(); 52a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 53a1c6015169815e90684917ac215926ae22e61d7aDiego Perez ArrayList<Integer> typesArray = Lists.newArrayList(); 54a1c6015169815e90684917ac215926ae22e61d7aDiego Perez ArrayList<float[]> pointsArray = Lists.newArrayList(); 55a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float[] points = new float[6]; 56a1c6015169815e90684917ac215926ae22e61d7aDiego Perez while (!iterator.isDone()) { 57a1c6015169815e90684917ac215926ae22e61d7aDiego Perez int type = iterator.currentSegment(points); 58a1c6015169815e90684917ac215926ae22e61d7aDiego Perez int nPoints = getNumberOfPoints(type) * 2; // 2 coordinates per point 59a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 60a1c6015169815e90684917ac215926ae22e61d7aDiego Perez typesArray.add(type); 61a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float[] itemPoints = new float[nPoints]; 62a1c6015169815e90684917ac215926ae22e61d7aDiego Perez System.arraycopy(points, 0, itemPoints, 0, nPoints); 63a1c6015169815e90684917ac215926ae22e61d7aDiego Perez pointsArray.add(itemPoints); 64a1c6015169815e90684917ac215926ae22e61d7aDiego Perez iterator.next(); 65a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 66a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 67a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mTypes = new int[typesArray.size()]; 68a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates = new float[mTypes.length][]; 69a1c6015169815e90684917ac215926ae22e61d7aDiego Perez for (int i = 0; i < typesArray.size(); i++) { 70a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mTypes[i] = typesArray.get(i); 71a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[i] = pointsArray.get(i); 72a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 73a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 74a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // Do measurement 75a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mSegmentsLength = new float[mTypes.length]; 76a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 77a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // Curves that we can reuse to estimate segments length 78a1c6015169815e90684917ac215926ae22e61d7aDiego Perez CubicCurve2D.Float cubicCurve = new CubicCurve2D.Float(); 79a1c6015169815e90684917ac215926ae22e61d7aDiego Perez QuadCurve2D.Float quadCurve = new QuadCurve2D.Float(); 80a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float lastX = 0; 81a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float lastY = 0; 82a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float totalLength = 0; 83a1c6015169815e90684917ac215926ae22e61d7aDiego Perez for (int i = 0; i < mTypes.length; i++) { 84a1c6015169815e90684917ac215926ae22e61d7aDiego Perez switch (mTypes[i]) { 85a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case PathIterator.SEG_CUBICTO: 86a1c6015169815e90684917ac215926ae22e61d7aDiego Perez cubicCurve.setCurve(lastX, lastY, 87a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[i][0], mCoordinates[i][1], mCoordinates[i][2], 88a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[i][3], lastX = mCoordinates[i][4], 89a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastY = mCoordinates[i][5]); 90a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mSegmentsLength[i] = 91a1c6015169815e90684917ac215926ae22e61d7aDiego Perez getFlatPathLength(cubicCurve.getPathIterator(null, PRECISION)); 92a1c6015169815e90684917ac215926ae22e61d7aDiego Perez break; 93a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case PathIterator.SEG_QUADTO: 94a1c6015169815e90684917ac215926ae22e61d7aDiego Perez quadCurve.setCurve(lastX, lastY, mCoordinates[i][0], mCoordinates[i][1], 95a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastX = mCoordinates[i][2], lastY = mCoordinates[i][3]); 96a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mSegmentsLength[i] = 97a1c6015169815e90684917ac215926ae22e61d7aDiego Perez getFlatPathLength(quadCurve.getPathIterator(null, PRECISION)); 98a1c6015169815e90684917ac215926ae22e61d7aDiego Perez break; 99a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case PathIterator.SEG_CLOSE: 100a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mSegmentsLength[i] = (float) Point2D.distance(lastX, lastY, 101a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastX = mCoordinates[0][0], 102a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastY = mCoordinates[0][1]); 103a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[i] = new float[2]; 104a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // We convert a SEG_CLOSE segment to a SEG_LINETO so we do not have to worry 105a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // about this special case in the rest of the code. 106a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mTypes[i] = PathIterator.SEG_LINETO; 107a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[i][0] = mCoordinates[0][0]; 108a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[i][1] = mCoordinates[0][1]; 109a1c6015169815e90684917ac215926ae22e61d7aDiego Perez break; 110a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case PathIterator.SEG_MOVETO: 111a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mSegmentsLength[i] = 0; 112a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastX = mCoordinates[i][0]; 113a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastY = mCoordinates[i][1]; 114a1c6015169815e90684917ac215926ae22e61d7aDiego Perez break; 115a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case PathIterator.SEG_LINETO: 116a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mSegmentsLength[i] = (float) Point2D.distance(lastX, lastY, mCoordinates[i][0], 117a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[i][1]); 118a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastX = mCoordinates[i][0]; 119a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastY = mCoordinates[i][1]; 120a1c6015169815e90684917ac215926ae22e61d7aDiego Perez default: 121a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 122a1c6015169815e90684917ac215926ae22e61d7aDiego Perez totalLength += mSegmentsLength[i]; 123a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 124a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 125a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mTotalLength = totalLength; 126a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 127a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 128a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private static void quadCurveSegment(float[] coords, float t0, float t1) { 129a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // Calculate X and Y at 0.5 (We'll use this to reconstruct the control point later) 130a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float mt = t0 + (t1 - t0) / 2; 131a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float mu = 1 - mt; 132a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float mx = mu * mu * coords[0] + 2 * mu * mt * coords[2] + mt * mt * coords[4]; 133a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float my = mu * mu * coords[1] + 2 * mu * mt * coords[3] + mt * mt * coords[5]; 134a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 135a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float u0 = 1 - t0; 136a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float u1 = 1 - t1; 137a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 138a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // coords at t0 139a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[0] = coords[0] * u0 * u0 + coords[2] * 2 * t0 * u0 + coords[4] * t0 * t0; 140a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[1] = coords[1] * u0 * u0 + coords[3] * 2 * t0 * u0 + coords[5] * t0 * t0; 141a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 142a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // coords at t1 143a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[4] = coords[0] * u1 * u1 + coords[2] * 2 * t1 * u1 + coords[4] * t1 * t1; 144a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[5] = coords[1] * u1 * u1 + coords[3] * 2 * t1 * u1 + coords[5] * t1 * t1; 145a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 146a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // estimated control point at t'=0.5 147a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[2] = 2 * mx - coords[0] / 2 - coords[4] / 2; 148a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[3] = 2 * my - coords[1] / 2 - coords[5] / 2; 149a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 150a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 151a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private static void cubicCurveSegment(float[] coords, float t0, float t1) { 152a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // http://stackoverflow.com/questions/11703283/cubic-bezier-curve-segment 153a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float u0 = 1 - t0; 154a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float u1 = 1 - t1; 155a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 156a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // Calculate the points at t0 and t1 for the quadratic curves formed by (P0, P1, P2) and 157a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // (P1, P2, P3) 158a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float qxa = coords[0] * u0 * u0 + coords[2] * 2 * t0 * u0 + coords[4] * t0 * t0; 159a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float qxb = coords[0] * u1 * u1 + coords[2] * 2 * t1 * u1 + coords[4] * t1 * t1; 160a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float qxc = coords[2] * u0 * u0 + coords[4] * 2 * t0 * u0 + coords[6] * t0 * t0; 161a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float qxd = coords[2] * u1 * u1 + coords[4] * 2 * t1 * u1 + coords[6] * t1 * t1; 162a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 163a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float qya = coords[1] * u0 * u0 + coords[3] * 2 * t0 * u0 + coords[5] * t0 * t0; 164a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float qyb = coords[1] * u1 * u1 + coords[3] * 2 * t1 * u1 + coords[5] * t1 * t1; 165a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float qyc = coords[3] * u0 * u0 + coords[5] * 2 * t0 * u0 + coords[7] * t0 * t0; 166a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float qyd = coords[3] * u1 * u1 + coords[5] * 2 * t1 * u1 + coords[7] * t1 * t1; 167a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 168a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // Linear interpolation 169a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[0] = qxa * u0 + qxc * t0; 170a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[1] = qya * u0 + qyc * t0; 171a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 172a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[2] = qxa * u1 + qxc * t1; 173a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[3] = qya * u1 + qyc * t1; 174a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 175a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[4] = qxb * u0 + qxd * t0; 176a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[5] = qyb * u0 + qyd * t0; 177a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 178a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[6] = qxb * u1 + qxd * t1; 179a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[7] = qyb * u1 + qyd * t1; 180a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 181a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 182a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 183a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Returns the end point of a given segment 184a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * 185a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * @param type the segment type 186a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * @param coords the segment coordinates array 187a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * @param point the return array where the point will be stored 188a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 189a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private static void getShapeEndPoint(int type, @NonNull float[] coords, @NonNull float[] 190a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point) { 191a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // start index of the end point for the segment type 192a1c6015169815e90684917ac215926ae22e61d7aDiego Perez int pointIndex = (getNumberOfPoints(type) - 1) * 2; 193a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point[0] = coords[pointIndex]; 194a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point[1] = coords[pointIndex + 1]; 195a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 196a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 197a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 198a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Returns the number of points stored in a coordinates array for the given segment type. 199a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 200a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private static int getNumberOfPoints(int segmentType) { 201a1c6015169815e90684917ac215926ae22e61d7aDiego Perez switch (segmentType) { 202a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case PathIterator.SEG_QUADTO: 203a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return 2; 204a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case PathIterator.SEG_CUBICTO: 205a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return 3; 206a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case PathIterator.SEG_CLOSE: 207a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return 0; 208a1c6015169815e90684917ac215926ae22e61d7aDiego Perez default: 209a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return 1; 210a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 211a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 212a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 213a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 214a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Returns the estimated length of a flat path. If the passed path is not flat (i.e. contains a 215a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * segment that is not {@link PathIterator#SEG_CLOSE}, {@link PathIterator#SEG_MOVETO} or {@link 216a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * PathIterator#SEG_LINETO} this method will fail. 217a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 218a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private static float getFlatPathLength(@NonNull PathIterator iterator) { 219a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float segment[] = new float[6]; 220a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float totalLength = 0; 221a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float[] previousPoint = new float[2]; 222a1c6015169815e90684917ac215926ae22e61d7aDiego Perez boolean isFirstPoint = true; 223a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 224a1c6015169815e90684917ac215926ae22e61d7aDiego Perez while (!iterator.isDone()) { 225a1c6015169815e90684917ac215926ae22e61d7aDiego Perez int type = iterator.currentSegment(segment); 226a1c6015169815e90684917ac215926ae22e61d7aDiego Perez assert type == PathIterator.SEG_LINETO || type == PathIterator.SEG_CLOSE || type == 227a1c6015169815e90684917ac215926ae22e61d7aDiego Perez PathIterator.SEG_MOVETO; 228a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 229a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // MoveTo shouldn't affect the length 230a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (!isFirstPoint && type != PathIterator.SEG_MOVETO) { 231a1c6015169815e90684917ac215926ae22e61d7aDiego Perez totalLength += Point2D.distance(previousPoint[0], previousPoint[1], segment[0], 232a1c6015169815e90684917ac215926ae22e61d7aDiego Perez segment[1]); 233a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } else { 234a1c6015169815e90684917ac215926ae22e61d7aDiego Perez isFirstPoint = false; 235a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 236a1c6015169815e90684917ac215926ae22e61d7aDiego Perez previousPoint[0] = segment[0]; 237a1c6015169815e90684917ac215926ae22e61d7aDiego Perez previousPoint[1] = segment[1]; 238a1c6015169815e90684917ac215926ae22e61d7aDiego Perez iterator.next(); 239a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 240a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 241a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return totalLength; 242a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 243a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 244a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 245a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Returns the estimated position along a path of the given length. 246a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 247a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private void getPointAtLength(int type, @NonNull float[] coords, float lastX, float 248a1c6015169815e90684917ac215926ae22e61d7aDiego Perez lastY, float t, @NonNull float[] point) { 249a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (type == PathIterator.SEG_LINETO) { 250a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point[0] = lastX + (coords[0] - lastX) * t; 251a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point[1] = lastY + (coords[1] - lastY) * t; 252a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // Return here, since we do not need a shape to estimate 253a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return; 254a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 255a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 256a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float[] curve = new float[8]; 257a1c6015169815e90684917ac215926ae22e61d7aDiego Perez int lastPointIndex = (getNumberOfPoints(type) - 1) * 2; 258a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 259a1c6015169815e90684917ac215926ae22e61d7aDiego Perez System.arraycopy(coords, 0, curve, 2, coords.length); 260a1c6015169815e90684917ac215926ae22e61d7aDiego Perez curve[0] = lastX; 261a1c6015169815e90684917ac215926ae22e61d7aDiego Perez curve[1] = lastY; 262a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (type == PathIterator.SEG_CUBICTO) { 263a1c6015169815e90684917ac215926ae22e61d7aDiego Perez cubicCurveSegment(curve, 0f, t); 264a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } else { 265a1c6015169815e90684917ac215926ae22e61d7aDiego Perez quadCurveSegment(curve, 0f, t); 266a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 267a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 268a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point[0] = curve[2 + lastPointIndex]; 269a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point[1] = curve[2 + lastPointIndex + 1]; 270a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 271a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 272a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public CachedPathIterator iterator() { 273a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return new CachedPathIterator(); 274a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 275a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 276a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 277a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Class that allows us to iterate over a path multiple times 278a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 279a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public class CachedPathIterator implements PathIterator { 280a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private int mNextIndex; 281a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 282a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 283a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Current segment type. 284a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * 285a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * @see PathIterator 286a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 287a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private int mCurrentType; 288a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 289a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 290a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Stores the coordinates array of the current segment. The number of points stored depends 291a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * on the segment type. 292a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * 293a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * @see PathIterator 294a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 295a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private float[] mCurrentCoords = new float[6]; 296a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private float mCurrentSegmentLength; 297a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 298a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 299a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Current segment length offset. When asking for the length of the current segment, the 300a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * length will be reduced by this amount. This is useful when we are only using portions of 301a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * the segment. 302a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * 303a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * @see #jumpToSegment(float) 304a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 305a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private float mOffsetLength; 306a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 307a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** Point where the current segment started */ 308a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private float[] mLastPoint = new float[2]; 309a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private boolean isIteratorDone; 310a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 311a1c6015169815e90684917ac215926ae22e61d7aDiego Perez private CachedPathIterator() { 312a1c6015169815e90684917ac215926ae22e61d7aDiego Perez next(); 313a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 314a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 315a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public float getCurrentSegmentLength() { 316a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return mCurrentSegmentLength; 317a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 318a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 319a1c6015169815e90684917ac215926ae22e61d7aDiego Perez @Override 320a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public int getWindingRule() { 321a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return mWindingRule; 322a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 323a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 324a1c6015169815e90684917ac215926ae22e61d7aDiego Perez @Override 325a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public boolean isDone() { 326a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return isIteratorDone; 327a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 328a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 329a1c6015169815e90684917ac215926ae22e61d7aDiego Perez @Override 330a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public void next() { 331a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (mNextIndex >= mTypes.length) { 332a1c6015169815e90684917ac215926ae22e61d7aDiego Perez isIteratorDone = true; 333a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return; 334a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 335a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 336a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (mNextIndex >= 1) { 337a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // We've already called next() once so there is a previous segment in this path. 338a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // We want to get the coordinates where the path ends. 339a1c6015169815e90684917ac215926ae22e61d7aDiego Perez getShapeEndPoint(mCurrentType, mCurrentCoords, mLastPoint); 340a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } else { 341a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // This is the first segment, no previous point so initialize to 0, 0 342a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mLastPoint[0] = mLastPoint[1] = 0f; 343a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 344a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCurrentType = mTypes[mNextIndex]; 345a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCurrentSegmentLength = mSegmentsLength[mNextIndex] - mOffsetLength; 346a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 347a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (mOffsetLength > 0f && (mCurrentType == SEG_CUBICTO || mCurrentType == SEG_QUADTO)) { 348a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // We need to skip part of the start of the current segment (because 349a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // mOffsetLength > 0) 350a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float[] points = new float[8]; 351a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 352a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (mNextIndex < 1) { 353a1c6015169815e90684917ac215926ae22e61d7aDiego Perez points[0] = points[1] = 0f; 354a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } else { 355a1c6015169815e90684917ac215926ae22e61d7aDiego Perez getShapeEndPoint(mTypes[mNextIndex - 1], mCoordinates[mNextIndex - 1], points); 356a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 357a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 358a1c6015169815e90684917ac215926ae22e61d7aDiego Perez System.arraycopy(mCoordinates[mNextIndex], 0, points, 2, 359a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[mNextIndex].length); 360a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float t0 = (mSegmentsLength[mNextIndex] - mCurrentSegmentLength) / 361a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mSegmentsLength[mNextIndex]; 362a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (mCurrentType == SEG_CUBICTO) { 363a1c6015169815e90684917ac215926ae22e61d7aDiego Perez cubicCurveSegment(points, t0, 1f); 364a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } else { 365a1c6015169815e90684917ac215926ae22e61d7aDiego Perez quadCurveSegment(points, t0, 1f); 366a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 367a1c6015169815e90684917ac215926ae22e61d7aDiego Perez System.arraycopy(points, 2, mCurrentCoords, 0, mCoordinates[mNextIndex].length); 368a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } else { 369a1c6015169815e90684917ac215926ae22e61d7aDiego Perez System.arraycopy(mCoordinates[mNextIndex], 0, mCurrentCoords, 0, 370a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[mNextIndex].length); 371a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 372a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 373a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mOffsetLength = 0f; 374a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mNextIndex++; 375a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 376a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 377a1c6015169815e90684917ac215926ae22e61d7aDiego Perez @Override 378a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public int currentSegment(float[] coords) { 379a1c6015169815e90684917ac215926ae22e61d7aDiego Perez System.arraycopy(mCurrentCoords, 0, coords, 0, getNumberOfPoints(mCurrentType) * 2); 380a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return mCurrentType; 381a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 382a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 383a1c6015169815e90684917ac215926ae22e61d7aDiego Perez @Override 384a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public int currentSegment(double[] coords) { 385a1c6015169815e90684917ac215926ae22e61d7aDiego Perez throw new UnsupportedOperationException(); 386a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 387a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 388a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 389a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Returns the point where the current segment ends 390a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 391a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public void getCurrentSegmentEnd(float[] point) { 392a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point[0] = mLastPoint[0]; 393a1c6015169815e90684917ac215926ae22e61d7aDiego Perez point[1] = mLastPoint[1]; 394a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 395a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 396a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 397a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Restarts the iterator and jumps all the segments of this path up to the length value. 398a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 399a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public void jumpToSegment(float length) { 400a1c6015169815e90684917ac215926ae22e61d7aDiego Perez isIteratorDone = false; 401a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (length <= 0f) { 402a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mNextIndex = 0; 403a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return; 404a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 405a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 406a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float accLength = 0; 407a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float lastPoint[] = new float[2]; 408a1c6015169815e90684917ac215926ae22e61d7aDiego Perez for (mNextIndex = 0; mNextIndex < mTypes.length; mNextIndex++) { 409a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float segmentLength = mSegmentsLength[mNextIndex]; 410a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (accLength + segmentLength >= length && mTypes[mNextIndex] != SEG_MOVETO) { 411a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float[] estimatedPoint = new float[2]; 412a1c6015169815e90684917ac215926ae22e61d7aDiego Perez getPointAtLength(mTypes[mNextIndex], 413a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCoordinates[mNextIndex], lastPoint[0], lastPoint[1], 414a1c6015169815e90684917ac215926ae22e61d7aDiego Perez (length - accLength) / segmentLength, 415a1c6015169815e90684917ac215926ae22e61d7aDiego Perez estimatedPoint); 416a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 417a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // This segment makes us go further than length so we go back one step, 418a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // set a moveto and offset the length of the next segment by the length 419a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // of this segment that we've already used. 420a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCurrentType = PathIterator.SEG_MOVETO; 421a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCurrentCoords[0] = estimatedPoint[0]; 422a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCurrentCoords[1] = estimatedPoint[1]; 423a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mCurrentSegmentLength = 0; 424a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 425a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // We need to offset next path length to account for the segment we've just 426a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // skipped. 427a1c6015169815e90684917ac215926ae22e61d7aDiego Perez mOffsetLength = length - accLength; 428a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return; 429a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 430a1c6015169815e90684917ac215926ae22e61d7aDiego Perez accLength += segmentLength; 431a1c6015169815e90684917ac215926ae22e61d7aDiego Perez getShapeEndPoint(mTypes[mNextIndex], mCoordinates[mNextIndex], lastPoint); 432a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 433a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 434a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 435a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 436a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Returns the current segment up to certain length. If the current segment is shorter than 437a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * length, then the whole segment is returned. The segment coordinates are copied into the 438a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * coords array. 439a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * 440a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * @return the segment type 441a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 442a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public int currentSegment(@NonNull float[] coords, float length) { 443a1c6015169815e90684917ac215926ae22e61d7aDiego Perez int type = currentSegment(coords); 444a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // If the length is greater than the current segment length, no need to find 445a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // the cut point. Same if this is a SEG_MOVETO. 446a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (mCurrentSegmentLength <= length || type == SEG_MOVETO) { 447a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return type; 448a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 449a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 450a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float t = length / getCurrentSegmentLength(); 451a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 452a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // We find at which offset the end point is located within the coords array and set 453a1c6015169815e90684917ac215926ae22e61d7aDiego Perez // a new end point to cut the segment short 454a1c6015169815e90684917ac215926ae22e61d7aDiego Perez switch (type) { 455a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case SEG_CUBICTO: 456a1c6015169815e90684917ac215926ae22e61d7aDiego Perez case SEG_QUADTO: 457a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float[] curve = new float[8]; 458a1c6015169815e90684917ac215926ae22e61d7aDiego Perez curve[0] = mLastPoint[0]; 459a1c6015169815e90684917ac215926ae22e61d7aDiego Perez curve[1] = mLastPoint[1]; 460a1c6015169815e90684917ac215926ae22e61d7aDiego Perez System.arraycopy(coords, 0, curve, 2, coords.length); 461a1c6015169815e90684917ac215926ae22e61d7aDiego Perez if (type == SEG_CUBICTO) { 462a1c6015169815e90684917ac215926ae22e61d7aDiego Perez cubicCurveSegment(curve, 0f, t); 463a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } else { 464a1c6015169815e90684917ac215926ae22e61d7aDiego Perez quadCurveSegment(curve, 0f, t); 465a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 466a1c6015169815e90684917ac215926ae22e61d7aDiego Perez System.arraycopy(curve, 2, coords, 0, coords.length); 467a1c6015169815e90684917ac215926ae22e61d7aDiego Perez break; 468a1c6015169815e90684917ac215926ae22e61d7aDiego Perez default: 469a1c6015169815e90684917ac215926ae22e61d7aDiego Perez float[] point = new float[2]; 470a1c6015169815e90684917ac215926ae22e61d7aDiego Perez getPointAtLength(type, coords, mLastPoint[0], mLastPoint[1], t, point); 471a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[0] = point[0]; 472a1c6015169815e90684917ac215926ae22e61d7aDiego Perez coords[1] = point[1]; 473a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 474a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 475a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return type; 476a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 477a1c6015169815e90684917ac215926ae22e61d7aDiego Perez 478a1c6015169815e90684917ac215926ae22e61d7aDiego Perez /** 479a1c6015169815e90684917ac215926ae22e61d7aDiego Perez * Returns the total length of the path 480a1c6015169815e90684917ac215926ae22e61d7aDiego Perez */ 481a1c6015169815e90684917ac215926ae22e61d7aDiego Perez public float getTotalLength() { 482a1c6015169815e90684917ac215926ae22e61d7aDiego Perez return mTotalLength; 483a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 484a1c6015169815e90684917ac215926ae22e61d7aDiego Perez } 485a1c6015169815e90684917ac215926ae22e61d7aDiego Perez} 486