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