134cbdb2e17271b31d1511d2773edd241fafede7dztenghui/* 234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Copyright (C) 2015 The Android Open Source Project 334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * in compliance with the License. You may obtain a copy of the License at 634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * http://www.apache.org/licenses/LICENSE-2.0 834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Unless required by applicable law or agreed to in writing, software distributed under the License 1034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 1134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * or implied. See the License for the specific language governing permissions and limitations under 1234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * the License. 1334cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 1434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 154fcaa70c2362e58a3fb30d140f0a0eeda8e35b44ztenghuipackage android.support.graphics.drawable; 1634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 1734cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport android.graphics.Path; 1834cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport android.util.Log; 1934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 2034cbdb2e17271b31d1511d2773edd241fafede7dztenghuiimport java.util.ArrayList; 2134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 2234cbdb2e17271b31d1511d2773edd241fafede7dztenghui// This class is a duplicate from the PathParser.java of frameworks/base, with slight 2334cbdb2e17271b31d1511d2773edd241fafede7dztenghui// update on incompatible API like copyOfRange(). 2434cbdb2e17271b31d1511d2773edd241fafede7dztenghuiclass PathParser { 2534cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static final String LOGTAG = "PathParser"; 2634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 2734cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Copy from Arrays.copyOfRange() which is only available from API level 9. 282c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu 2934cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 3034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Copies elements from {@code original} into a new array, from indexes start (inclusive) to 3134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * end (exclusive). The original order of elements is preserved. 3234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * If {@code end} is greater than {@code original.length}, the result is padded 3334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * with the value {@code 0.0f}. 3434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 3534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param original the original array 362c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param start the start index, inclusive 372c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param end the end index, exclusive 3834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return the new array 3934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length} 402c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @throws IllegalArgumentException if {@code start > end} 412c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @throws NullPointerException if {@code original == null} 4234cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 4334cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static float[] copyOfRange(float[] original, int start, int end) { 4434cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (start > end) { 4534cbdb2e17271b31d1511d2773edd241fafede7dztenghui throw new IllegalArgumentException(); 4634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 4734cbdb2e17271b31d1511d2773edd241fafede7dztenghui int originalLength = original.length; 4834cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (start < 0 || start > originalLength) { 4934cbdb2e17271b31d1511d2773edd241fafede7dztenghui throw new ArrayIndexOutOfBoundsException(); 5034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 5134cbdb2e17271b31d1511d2773edd241fafede7dztenghui int resultLength = end - start; 5234cbdb2e17271b31d1511d2773edd241fafede7dztenghui int copyLength = Math.min(resultLength, originalLength - start); 5334cbdb2e17271b31d1511d2773edd241fafede7dztenghui float[] result = new float[resultLength]; 5434cbdb2e17271b31d1511d2773edd241fafede7dztenghui System.arraycopy(original, start, result, 0, copyLength); 5534cbdb2e17271b31d1511d2773edd241fafede7dztenghui return result; 5634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 5734cbdb2e17271b31d1511d2773edd241fafede7dztenghui 5834cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 5934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param pathData The string representing a path, the same as "d" string in svg file. 6034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return the generated Path object. 6134cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 6234cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static Path createPathFromPathData(String pathData) { 6334cbdb2e17271b31d1511d2773edd241fafede7dztenghui Path path = new Path(); 6434cbdb2e17271b31d1511d2773edd241fafede7dztenghui PathDataNode[] nodes = createNodesFromPathData(pathData); 6534cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (nodes != null) { 6634cbdb2e17271b31d1511d2773edd241fafede7dztenghui try { 6734cbdb2e17271b31d1511d2773edd241fafede7dztenghui PathDataNode.nodesToPath(nodes, path); 6834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } catch (RuntimeException e) { 6934cbdb2e17271b31d1511d2773edd241fafede7dztenghui throw new RuntimeException("Error in parsing " + pathData, e); 7034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 7134cbdb2e17271b31d1511d2773edd241fafede7dztenghui return path; 7234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 7334cbdb2e17271b31d1511d2773edd241fafede7dztenghui return null; 7434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 7534cbdb2e17271b31d1511d2773edd241fafede7dztenghui 7634cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 7734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param pathData The string representing a path, the same as "d" string in svg file. 7834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return an array of the PathDataNode. 7934cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 8034cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static PathDataNode[] createNodesFromPathData(String pathData) { 8134cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (pathData == null) { 8234cbdb2e17271b31d1511d2773edd241fafede7dztenghui return null; 8334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 8434cbdb2e17271b31d1511d2773edd241fafede7dztenghui int start = 0; 8534cbdb2e17271b31d1511d2773edd241fafede7dztenghui int end = 1; 8634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 8734cbdb2e17271b31d1511d2773edd241fafede7dztenghui ArrayList<PathDataNode> list = new ArrayList<PathDataNode>(); 8834cbdb2e17271b31d1511d2773edd241fafede7dztenghui while (end < pathData.length()) { 8934cbdb2e17271b31d1511d2773edd241fafede7dztenghui end = nextStart(pathData, end); 9034cbdb2e17271b31d1511d2773edd241fafede7dztenghui String s = pathData.substring(start, end).trim(); 9134cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (s.length() > 0) { 9234cbdb2e17271b31d1511d2773edd241fafede7dztenghui float[] val = getFloats(s); 9334cbdb2e17271b31d1511d2773edd241fafede7dztenghui addNode(list, s.charAt(0), val); 9434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 9534cbdb2e17271b31d1511d2773edd241fafede7dztenghui 9634cbdb2e17271b31d1511d2773edd241fafede7dztenghui start = end; 9734cbdb2e17271b31d1511d2773edd241fafede7dztenghui end++; 9834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 9934cbdb2e17271b31d1511d2773edd241fafede7dztenghui if ((end - start) == 1 && start < pathData.length()) { 10034cbdb2e17271b31d1511d2773edd241fafede7dztenghui addNode(list, pathData.charAt(start), new float[0]); 10134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 10234cbdb2e17271b31d1511d2773edd241fafede7dztenghui return list.toArray(new PathDataNode[list.size()]); 10334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 10434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 10534cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 10634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param source The array of PathDataNode to be duplicated. 10734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return a deep copy of the <code>source</code>. 10834cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 10934cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static PathDataNode[] deepCopyNodes(PathDataNode[] source) { 11034cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (source == null) { 11134cbdb2e17271b31d1511d2773edd241fafede7dztenghui return null; 11234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 11334cbdb2e17271b31d1511d2773edd241fafede7dztenghui PathDataNode[] copy = new PathParser.PathDataNode[source.length]; 1142c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu for (int i = 0; i < source.length; i++) { 11534cbdb2e17271b31d1511d2773edd241fafede7dztenghui copy[i] = new PathDataNode(source[i]); 11634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 11734cbdb2e17271b31d1511d2773edd241fafede7dztenghui return copy; 11834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 11934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 12034cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 12134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param nodesFrom The source path represented in an array of PathDataNode 1222c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param nodesTo The target path represented in an array of PathDataNode 12334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code> 12434cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 12534cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) { 12634cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (nodesFrom == null || nodesTo == null) { 12734cbdb2e17271b31d1511d2773edd241fafede7dztenghui return false; 12834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 12934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 13034cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (nodesFrom.length != nodesTo.length) { 13134cbdb2e17271b31d1511d2773edd241fafede7dztenghui return false; 13234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 13334cbdb2e17271b31d1511d2773edd241fafede7dztenghui 1342c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu for (int i = 0; i < nodesFrom.length; i++) { 135d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu if (nodesFrom[i].type != nodesTo[i].type 136d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu || nodesFrom[i].params.length != nodesTo[i].params.length) { 13734cbdb2e17271b31d1511d2773edd241fafede7dztenghui return false; 13834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 13934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 14034cbdb2e17271b31d1511d2773edd241fafede7dztenghui return true; 14134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 14234cbdb2e17271b31d1511d2773edd241fafede7dztenghui 14334cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 14434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Update the target's data to match the source. 14534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Before calling this, make sure canMorph(target, source) is true. 14634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 14734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param target The target path represented in an array of PathDataNode 14834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param source The source path represented in an array of PathDataNode 14934cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 15034cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static void updateNodes(PathDataNode[] target, PathDataNode[] source) { 1512c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu for (int i = 0; i < source.length; i++) { 152d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu target[i].type = source[i].type; 1532c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu for (int j = 0; j < source[i].params.length; j++) { 154d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu target[i].params[j] = source[i].params[j]; 15534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 15634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 15734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 15834cbdb2e17271b31d1511d2773edd241fafede7dztenghui 15934cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static int nextStart(String s, int end) { 16034cbdb2e17271b31d1511d2773edd241fafede7dztenghui char c; 16134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 16234cbdb2e17271b31d1511d2773edd241fafede7dztenghui while (end < s.length()) { 16334cbdb2e17271b31d1511d2773edd241fafede7dztenghui c = s.charAt(end); 16434cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Note that 'e' or 'E' are not valid path commands, but could be 16534cbdb2e17271b31d1511d2773edd241fafede7dztenghui // used for floating point numbers' scientific notation. 16634cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Therefore, when searching for next command, we should ignore 'e' 16734cbdb2e17271b31d1511d2773edd241fafede7dztenghui // and 'E'. 16834cbdb2e17271b31d1511d2773edd241fafede7dztenghui if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0)) 16934cbdb2e17271b31d1511d2773edd241fafede7dztenghui && c != 'e' && c != 'E') { 17034cbdb2e17271b31d1511d2773edd241fafede7dztenghui return end; 17134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 17234cbdb2e17271b31d1511d2773edd241fafede7dztenghui end++; 17334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 17434cbdb2e17271b31d1511d2773edd241fafede7dztenghui return end; 17534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 17634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 17734cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) { 17834cbdb2e17271b31d1511d2773edd241fafede7dztenghui list.add(new PathDataNode(cmd, val)); 17934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 18034cbdb2e17271b31d1511d2773edd241fafede7dztenghui 18134cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static class ExtractFloatResult { 18234cbdb2e17271b31d1511d2773edd241fafede7dztenghui // We need to return the position of the next separator and whether the 18334cbdb2e17271b31d1511d2773edd241fafede7dztenghui // next float starts with a '-' or a '.'. 18434cbdb2e17271b31d1511d2773edd241fafede7dztenghui int mEndPosition; 18534cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean mEndWithNegOrDot; 18634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 18734cbdb2e17271b31d1511d2773edd241fafede7dztenghui 18834cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 18934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Parse the floats in the string. 19034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * This is an optimized version of parseFloat(s.split(",|\\s")); 19134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 19234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param s the string containing a command and list of floats 19334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return array of floats 19434cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 19534cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static float[] getFloats(String s) { 19634cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') { 19734cbdb2e17271b31d1511d2773edd241fafede7dztenghui return new float[0]; 19834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 19934cbdb2e17271b31d1511d2773edd241fafede7dztenghui try { 20034cbdb2e17271b31d1511d2773edd241fafede7dztenghui float[] results = new float[s.length()]; 20134cbdb2e17271b31d1511d2773edd241fafede7dztenghui int count = 0; 20234cbdb2e17271b31d1511d2773edd241fafede7dztenghui int startPosition = 1; 20334cbdb2e17271b31d1511d2773edd241fafede7dztenghui int endPosition = 0; 20434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 20534cbdb2e17271b31d1511d2773edd241fafede7dztenghui ExtractFloatResult result = new ExtractFloatResult(); 20634cbdb2e17271b31d1511d2773edd241fafede7dztenghui int totalLength = s.length(); 20734cbdb2e17271b31d1511d2773edd241fafede7dztenghui 20834cbdb2e17271b31d1511d2773edd241fafede7dztenghui // The startPosition should always be the first character of the 20934cbdb2e17271b31d1511d2773edd241fafede7dztenghui // current number, and endPosition is the character after the current 21034cbdb2e17271b31d1511d2773edd241fafede7dztenghui // number. 21134cbdb2e17271b31d1511d2773edd241fafede7dztenghui while (startPosition < totalLength) { 21234cbdb2e17271b31d1511d2773edd241fafede7dztenghui extract(s, startPosition, result); 21334cbdb2e17271b31d1511d2773edd241fafede7dztenghui endPosition = result.mEndPosition; 21434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 21534cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (startPosition < endPosition) { 21634cbdb2e17271b31d1511d2773edd241fafede7dztenghui results[count++] = Float.parseFloat( 21734cbdb2e17271b31d1511d2773edd241fafede7dztenghui s.substring(startPosition, endPosition)); 21834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 21934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 22034cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (result.mEndWithNegOrDot) { 22134cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Keep the '-' or '.' sign with next number. 22234cbdb2e17271b31d1511d2773edd241fafede7dztenghui startPosition = endPosition; 22334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } else { 22434cbdb2e17271b31d1511d2773edd241fafede7dztenghui startPosition = endPosition + 1; 22534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 22634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 22734cbdb2e17271b31d1511d2773edd241fafede7dztenghui return copyOfRange(results, 0, count); 22834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } catch (NumberFormatException e) { 22934cbdb2e17271b31d1511d2773edd241fafede7dztenghui throw new RuntimeException("error in parsing \"" + s + "\"", e); 23034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 23134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 23234cbdb2e17271b31d1511d2773edd241fafede7dztenghui 23334cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 23434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Calculate the position of the next comma or space or negative sign 2352c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * 2362c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param s the string to search 2372c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param start the position to start searching 23834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param result the result of the extraction, including the position of the 2392c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * the starting position of next number, whether it is ending with a '-'. 24034cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 24134cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void extract(String s, int start, ExtractFloatResult result) { 24234cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Now looking for ' ', ',', '.' or '-' from the start. 24334cbdb2e17271b31d1511d2773edd241fafede7dztenghui int currentIndex = start; 24434cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean foundSeparator = false; 24534cbdb2e17271b31d1511d2773edd241fafede7dztenghui result.mEndWithNegOrDot = false; 24634cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean secondDot = false; 24734cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean isExponential = false; 24834cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (; currentIndex < s.length(); currentIndex++) { 24934cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean isPrevExponential = isExponential; 25034cbdb2e17271b31d1511d2773edd241fafede7dztenghui isExponential = false; 25134cbdb2e17271b31d1511d2773edd241fafede7dztenghui char currentChar = s.charAt(currentIndex); 25234cbdb2e17271b31d1511d2773edd241fafede7dztenghui switch (currentChar) { 25334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case ' ': 25434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case ',': 25534cbdb2e17271b31d1511d2773edd241fafede7dztenghui foundSeparator = true; 25634cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 25734cbdb2e17271b31d1511d2773edd241fafede7dztenghui case '-': 25834cbdb2e17271b31d1511d2773edd241fafede7dztenghui // The negative sign following a 'e' or 'E' is not a separator. 25934cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (currentIndex != start && !isPrevExponential) { 26034cbdb2e17271b31d1511d2773edd241fafede7dztenghui foundSeparator = true; 26134cbdb2e17271b31d1511d2773edd241fafede7dztenghui result.mEndWithNegOrDot = true; 26234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 26334cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 26434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case '.': 26534cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (!secondDot) { 26634cbdb2e17271b31d1511d2773edd241fafede7dztenghui secondDot = true; 26734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } else { 26834cbdb2e17271b31d1511d2773edd241fafede7dztenghui // This is the second dot, and it is considered as a separator. 26934cbdb2e17271b31d1511d2773edd241fafede7dztenghui foundSeparator = true; 27034cbdb2e17271b31d1511d2773edd241fafede7dztenghui result.mEndWithNegOrDot = true; 27134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 27234cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 27334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'e': 27434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'E': 27534cbdb2e17271b31d1511d2773edd241fafede7dztenghui isExponential = true; 27634cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 27734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 27834cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (foundSeparator) { 27934cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 28034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 28134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 28234cbdb2e17271b31d1511d2773edd241fafede7dztenghui // When there is nothing found, then we put the end position to the end 28334cbdb2e17271b31d1511d2773edd241fafede7dztenghui // of the string. 28434cbdb2e17271b31d1511d2773edd241fafede7dztenghui result.mEndPosition = currentIndex; 28534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 28634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 28734cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 28834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Each PathDataNode represents one command in the "d" attribute of the svg 28934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * file. 29034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * An array of PathDataNode can represent the whole "d" attribute. 29134cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 29234cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static class PathDataNode { 293d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu /*package*/ 294d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu char type; 295d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu float[] params; 29634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 29734cbdb2e17271b31d1511d2773edd241fafede7dztenghui private PathDataNode(char type, float[] params) { 298d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu this.type = type; 299d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu this.params = params; 30034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 30134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 30234cbdb2e17271b31d1511d2773edd241fafede7dztenghui private PathDataNode(PathDataNode n) { 303d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu type = n.type; 304d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu params = copyOfRange(n.params, 0, n.params.length); 30534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 30634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 30734cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 30834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Convert an array of PathDataNode to Path. 30934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 31034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param node The source array of PathDataNode. 31134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param path The target Path object. 31234cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 31334cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static void nodesToPath(PathDataNode[] node, Path path) { 31434cbdb2e17271b31d1511d2773edd241fafede7dztenghui float[] current = new float[6]; 31534cbdb2e17271b31d1511d2773edd241fafede7dztenghui char previousCommand = 'm'; 31634cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int i = 0; i < node.length; i++) { 317d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu addCommand(path, current, previousCommand, node[i].type, node[i].params); 318d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu previousCommand = node[i].type; 31934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 32034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 32134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 32234cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 32334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * The current PathDataNode will be interpolated between the 32434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * <code>nodeFrom</code> and <code>nodeTo</code> according to the 32534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * <code>fraction</code>. 32634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 32734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param nodeFrom The start value as a PathDataNode. 3282c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param nodeTo The end value as a PathDataNode 32934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param fraction The fraction to interpolate. 33034cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 33134cbdb2e17271b31d1511d2773edd241fafede7dztenghui public void interpolatePathDataNode(PathDataNode nodeFrom, 3322c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu PathDataNode nodeTo, float fraction) { 333d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu for (int i = 0; i < nodeFrom.params.length; i++) { 334d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu params[i] = nodeFrom.params[i] * (1 - fraction) 335d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu + nodeTo.params[i] * fraction; 33634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 33734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 33834cbdb2e17271b31d1511d2773edd241fafede7dztenghui 33934cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void addCommand(Path path, float[] current, 3402c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu char previousCmd, char cmd, float[] val) { 34134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 34234cbdb2e17271b31d1511d2773edd241fafede7dztenghui int incr = 2; 34334cbdb2e17271b31d1511d2773edd241fafede7dztenghui float currentX = current[0]; 34434cbdb2e17271b31d1511d2773edd241fafede7dztenghui float currentY = current[1]; 34534cbdb2e17271b31d1511d2773edd241fafede7dztenghui float ctrlPointX = current[2]; 34634cbdb2e17271b31d1511d2773edd241fafede7dztenghui float ctrlPointY = current[3]; 34734cbdb2e17271b31d1511d2773edd241fafede7dztenghui float currentSegmentStartX = current[4]; 34834cbdb2e17271b31d1511d2773edd241fafede7dztenghui float currentSegmentStartY = current[5]; 34934cbdb2e17271b31d1511d2773edd241fafede7dztenghui float reflectiveCtrlPointX; 35034cbdb2e17271b31d1511d2773edd241fafede7dztenghui float reflectiveCtrlPointY; 35134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 35234cbdb2e17271b31d1511d2773edd241fafede7dztenghui switch (cmd) { 35334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'z': 35434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'Z': 35534cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.close(); 35634cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Path is closed here, but we need to move the pen to the 35734cbdb2e17271b31d1511d2773edd241fafede7dztenghui // closed position. So we cache the segment's starting position, 35834cbdb2e17271b31d1511d2773edd241fafede7dztenghui // and restore it here. 35934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = currentSegmentStartX; 36034cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = currentSegmentStartY; 36134cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentSegmentStartX; 36234cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentSegmentStartY; 36334cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.moveTo(currentX, currentY); 36434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 36534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'm': 36634cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'M': 36734cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'l': 36834cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'L': 36934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 't': 37034cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'T': 37134cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 2; 37234cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 37334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'h': 37434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'H': 37534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'v': 37634cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'V': 37734cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 1; 37834cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 37934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'c': 38034cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'C': 38134cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 6; 38234cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 38334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 's': 38434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'S': 38534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'q': 38634cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'Q': 38734cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 4; 38834cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 38934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'a': 39034cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'A': 39134cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 7; 39234cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 39334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 39434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 39534cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int k = 0; k < val.length; k += incr) { 39634cbdb2e17271b31d1511d2773edd241fafede7dztenghui switch (cmd) { 39734cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'm': // moveto - Start a new sub-path (relative) 39834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 0]; 39934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 1]; 4002c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu if (k > 0) { 4012c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu // According to the spec, if a moveto is followed by multiple 4022c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu // pairs of coordinates, the subsequent pairs are treated as 4032c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu // implicit lineto commands. 4042c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu path.rLineTo(val[k + 0], val[k + 1]); 4052c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu } else { 4062c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu path.rMoveTo(val[k + 0], val[k + 1]); 4072c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu currentSegmentStartX = currentX; 4082c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu currentSegmentStartY = currentY; 4092c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu } 41034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 41134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'M': // moveto - Start a new sub-path 41234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 0]; 41334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 1]; 4142c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu if (k > 0) { 4152c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu // According to the spec, if a moveto is followed by multiple 4162c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu // pairs of coordinates, the subsequent pairs are treated as 4172c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu // implicit lineto commands. 4182c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu path.lineTo(val[k + 0], val[k + 1]); 4192c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu } else { 4202c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu path.moveTo(val[k + 0], val[k + 1]); 4212c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu currentSegmentStartX = currentX; 4222c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu currentSegmentStartY = currentY; 4232c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu } 42434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 42534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'l': // lineto - Draw a line from the current point (relative) 42634cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rLineTo(val[k + 0], val[k + 1]); 42734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 0]; 42834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 1]; 42934cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 43034cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'L': // lineto - Draw a line from the current point 43134cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.lineTo(val[k + 0], val[k + 1]); 43234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 0]; 43334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 1]; 43434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 43534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'h': // horizontal lineto - Draws a horizontal line (relative) 43634cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rLineTo(val[k + 0], 0); 43734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 0]; 43834cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 43934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'H': // horizontal lineto - Draws a horizontal line 44034cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.lineTo(val[k + 0], currentY); 44134cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 0]; 44234cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 44334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'v': // vertical lineto - Draws a vertical line from the current point (r) 44434cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rLineTo(0, val[k + 0]); 44534cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 0]; 44634cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 44734cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'V': // vertical lineto - Draws a vertical line from the current point 44834cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.lineTo(currentX, val[k + 0]); 44934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 0]; 45034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 45134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'c': // curveto - Draws a cubic Bézier curve (relative) 45234cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], 45334cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 4], val[k + 5]); 45434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 45534cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX + val[k + 2]; 45634cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY + val[k + 3]; 45734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 4]; 45834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 5]; 45934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 46034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 46134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'C': // curveto - Draws a cubic Bézier curve 46234cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], 46334cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 4], val[k + 5]); 46434cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 4]; 46534cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 5]; 46634cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = val[k + 2]; 46734cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = val[k + 3]; 46834cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 46934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp) 47034cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = 0; 47134cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = 0; 47234cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (previousCmd == 'c' || previousCmd == 's' 47334cbdb2e17271b31d1511d2773edd241fafede7dztenghui || previousCmd == 'C' || previousCmd == 'S') { 47434cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = currentX - ctrlPointX; 47534cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = currentY - ctrlPointY; 47634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 47734cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, 47834cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], val[k + 1], 47934cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 2], val[k + 3]); 48034cbdb2e17271b31d1511d2773edd241fafede7dztenghui 48134cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX + val[k + 0]; 48234cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY + val[k + 1]; 48334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 2]; 48434cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 3]; 48534cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 48634cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp) 48734cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = currentX; 48834cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = currentY; 48934cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (previousCmd == 'c' || previousCmd == 's' 49034cbdb2e17271b31d1511d2773edd241fafede7dztenghui || previousCmd == 'C' || previousCmd == 'S') { 49134cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = 2 * currentX - ctrlPointX; 49234cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = 2 * currentY - ctrlPointY; 49334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 49434cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, 49534cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], val[k + 1], val[k + 2], val[k + 3]); 49634cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = val[k + 0]; 49734cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = val[k + 1]; 49834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 2]; 49934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 3]; 50034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 50134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'q': // Draws a quadratic Bézier (relative) 50234cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); 50334cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX + val[k + 0]; 50434cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY + val[k + 1]; 50534cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 2]; 50634cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 3]; 50734cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 50834cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'Q': // Draws a quadratic Bézier 50934cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); 51034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = val[k + 0]; 51134cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = val[k + 1]; 51234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 2]; 51334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 3]; 51434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 51534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 't': // Draws a quadratic Bézier curve(reflective control point)(relative) 51634cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = 0; 51734cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = 0; 51834cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (previousCmd == 'q' || previousCmd == 't' 51934cbdb2e17271b31d1511d2773edd241fafede7dztenghui || previousCmd == 'Q' || previousCmd == 'T') { 52034cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = currentX - ctrlPointX; 52134cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = currentY - ctrlPointY; 52234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 52334cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, 52434cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], val[k + 1]); 52534cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX + reflectiveCtrlPointX; 52634cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY + reflectiveCtrlPointY; 52734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 0]; 52834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 1]; 52934cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 53034cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'T': // Draws a quadratic Bézier curve (reflective control point) 53134cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = currentX; 53234cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = currentY; 53334cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (previousCmd == 'q' || previousCmd == 't' 53434cbdb2e17271b31d1511d2773edd241fafede7dztenghui || previousCmd == 'Q' || previousCmd == 'T') { 53534cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = 2 * currentX - ctrlPointX; 53634cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = 2 * currentY - ctrlPointY; 53734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 53834cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, 53934cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], val[k + 1]); 54034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = reflectiveCtrlPointX; 54134cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = reflectiveCtrlPointY; 54234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 0]; 54334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 1]; 54434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 54534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'a': // Draws an elliptical arc 54634cbdb2e17271b31d1511d2773edd241fafede7dztenghui // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) 54734cbdb2e17271b31d1511d2773edd241fafede7dztenghui drawArc(path, 54834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX, 54934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY, 55034cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 5] + currentX, 55134cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 6] + currentY, 55234cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], 55334cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 1], 55434cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 2], 55534cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 3] != 0, 55634cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 4] != 0); 55734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 5]; 55834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 6]; 55934cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX; 56034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY; 56134cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 56234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'A': // Draws an elliptical arc 56334cbdb2e17271b31d1511d2773edd241fafede7dztenghui drawArc(path, 56434cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX, 56534cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY, 56634cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 5], 56734cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 6], 56834cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], 56934cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 1], 57034cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 2], 57134cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 3] != 0, 57234cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 4] != 0); 57334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 5]; 57434cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 6]; 57534cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX; 57634cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY; 57734cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 57834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 57934cbdb2e17271b31d1511d2773edd241fafede7dztenghui previousCmd = cmd; 58034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 58134cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[0] = currentX; 58234cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[1] = currentY; 58334cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[2] = ctrlPointX; 58434cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[3] = ctrlPointY; 58534cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[4] = currentSegmentStartX; 58634cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[5] = currentSegmentStartY; 58734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 58834cbdb2e17271b31d1511d2773edd241fafede7dztenghui 58934cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void drawArc(Path p, 5902c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu float x0, 5912c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu float y0, 5922c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu float x1, 5932c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu float y1, 5942c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu float a, 5952c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu float b, 5962c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu float theta, 5972c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu boolean isMoreThanHalf, 5982c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu boolean isPositiveArc) { 59934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 60034cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Convert rotation angle from degrees to radians */ 60134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double thetaD = Math.toRadians(theta); 60234cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Pre-compute rotation matrix entries */ 60334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cosTheta = Math.cos(thetaD); 60434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sinTheta = Math.sin(thetaD); 60534cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Transform (x0, y0) and (x1, y1) into unit space */ 60634cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* using (inverse) rotation, followed by (inverse) scale */ 60734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double x0p = (x0 * cosTheta + y0 * sinTheta) / a; 60834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double y0p = (-x0 * sinTheta + y0 * cosTheta) / b; 60934cbdb2e17271b31d1511d2773edd241fafede7dztenghui double x1p = (x1 * cosTheta + y1 * sinTheta) / a; 61034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double y1p = (-x1 * sinTheta + y1 * cosTheta) / b; 61134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 61234cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Compute differences and averages */ 61334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double dx = x0p - x1p; 61434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double dy = y0p - y1p; 61534cbdb2e17271b31d1511d2773edd241fafede7dztenghui double xm = (x0p + x1p) / 2; 61634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ym = (y0p + y1p) / 2; 61734cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Solve for intersecting unit circles */ 61834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double dsq = dx * dx + dy * dy; 61934cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (dsq == 0.0) { 62034cbdb2e17271b31d1511d2773edd241fafede7dztenghui Log.w(LOGTAG, " Points are coincident"); 62134cbdb2e17271b31d1511d2773edd241fafede7dztenghui return; /* Points are coincident */ 62234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 62334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double disc = 1.0 / dsq - 1.0 / 4.0; 62434cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (disc < 0.0) { 62534cbdb2e17271b31d1511d2773edd241fafede7dztenghui Log.w(LOGTAG, "Points are too far apart " + dsq); 62634cbdb2e17271b31d1511d2773edd241fafede7dztenghui float adjust = (float) (Math.sqrt(dsq) / 1.99999); 62734cbdb2e17271b31d1511d2773edd241fafede7dztenghui drawArc(p, x0, y0, x1, y1, a * adjust, 62834cbdb2e17271b31d1511d2773edd241fafede7dztenghui b * adjust, theta, isMoreThanHalf, isPositiveArc); 62934cbdb2e17271b31d1511d2773edd241fafede7dztenghui return; /* Points are too far apart */ 63034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 63134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double s = Math.sqrt(disc); 63234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sdx = s * dx; 63334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sdy = s * dy; 63434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cx; 63534cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cy; 63634cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (isMoreThanHalf == isPositiveArc) { 63734cbdb2e17271b31d1511d2773edd241fafede7dztenghui cx = xm - sdy; 63834cbdb2e17271b31d1511d2773edd241fafede7dztenghui cy = ym + sdx; 63934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } else { 64034cbdb2e17271b31d1511d2773edd241fafede7dztenghui cx = xm + sdy; 64134cbdb2e17271b31d1511d2773edd241fafede7dztenghui cy = ym - sdx; 64234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 64334cbdb2e17271b31d1511d2773edd241fafede7dztenghui 64434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double eta0 = Math.atan2((y0p - cy), (x0p - cx)); 64534cbdb2e17271b31d1511d2773edd241fafede7dztenghui 64634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double eta1 = Math.atan2((y1p - cy), (x1p - cx)); 64734cbdb2e17271b31d1511d2773edd241fafede7dztenghui 64834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sweep = (eta1 - eta0); 64934cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (isPositiveArc != (sweep >= 0)) { 65034cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (sweep > 0) { 65134cbdb2e17271b31d1511d2773edd241fafede7dztenghui sweep -= 2 * Math.PI; 65234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } else { 65334cbdb2e17271b31d1511d2773edd241fafede7dztenghui sweep += 2 * Math.PI; 65434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 65534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 65634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 65734cbdb2e17271b31d1511d2773edd241fafede7dztenghui cx *= a; 65834cbdb2e17271b31d1511d2773edd241fafede7dztenghui cy *= b; 65934cbdb2e17271b31d1511d2773edd241fafede7dztenghui double tcx = cx; 66034cbdb2e17271b31d1511d2773edd241fafede7dztenghui cx = cx * cosTheta - cy * sinTheta; 66134cbdb2e17271b31d1511d2773edd241fafede7dztenghui cy = tcx * sinTheta + cy * cosTheta; 66234cbdb2e17271b31d1511d2773edd241fafede7dztenghui 66334cbdb2e17271b31d1511d2773edd241fafede7dztenghui arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep); 66434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 66534cbdb2e17271b31d1511d2773edd241fafede7dztenghui 66634cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 66734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Converts an arc to cubic Bezier segments and records them in p. 66834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 6692c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param p The target for the cubic Bezier segments 6702c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param cx The x coordinate center of the ellipse 6712c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param cy The y coordinate center of the ellipse 6722c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param a The radius of the ellipse in the horizontal direction 6732c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param b The radius of the ellipse in the vertical direction 6742c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param e1x E(eta1) x coordinate of the starting point of the arc 6752c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu * @param e1y E(eta2) y coordinate of the starting point of the arc 67634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane 67734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param start The start angle of the arc on the ellipse 67834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse 67934cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 68034cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void arcToBezier(Path p, 6812c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double cx, 6822c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double cy, 6832c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double a, 6842c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double b, 6852c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double e1x, 6862c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double e1y, 6872c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double theta, 6882c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double start, 6892c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu double sweep) { 69034cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html 69134cbdb2e17271b31d1511d2773edd241fafede7dztenghui // and http://www.spaceroots.org/documents/ellipse/node22.html 69234cbdb2e17271b31d1511d2773edd241fafede7dztenghui 69334cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Maximum of 45 degrees per cubic Bezier segment 6942c3c8bff4c669316cdc2db24b72d9ac3f9b33725Teng-Hui Zhu int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI)); 69534cbdb2e17271b31d1511d2773edd241fafede7dztenghui 69634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double eta1 = start; 69734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cosTheta = Math.cos(theta); 69834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sinTheta = Math.sin(theta); 69934cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cosEta1 = Math.cos(eta1); 70034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sinEta1 = Math.sin(eta1); 70134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1); 70234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1); 70334cbdb2e17271b31d1511d2773edd241fafede7dztenghui 70434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double anglePerSegment = sweep / numSegments; 70534cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int i = 0; i < numSegments; i++) { 70634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double eta2 = eta1 + anglePerSegment; 70734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sinEta2 = Math.sin(eta2); 70834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cosEta2 = Math.cos(eta2); 70934cbdb2e17271b31d1511d2773edd241fafede7dztenghui double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2); 71034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2); 71134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2; 71234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2; 71334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double tanDiff2 = Math.tan((eta2 - eta1) / 2); 71434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double alpha = 71534cbdb2e17271b31d1511d2773edd241fafede7dztenghui Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3; 71634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double q1x = e1x + alpha * ep1x; 71734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double q1y = e1y + alpha * ep1y; 71834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double q2x = e2x - alpha * ep2x; 71934cbdb2e17271b31d1511d2773edd241fafede7dztenghui double q2y = e2y - alpha * ep2y; 72034cbdb2e17271b31d1511d2773edd241fafede7dztenghui 72134cbdb2e17271b31d1511d2773edd241fafede7dztenghui p.cubicTo((float) q1x, 72234cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) q1y, 72334cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) q2x, 72434cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) q2y, 72534cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) e2x, 72634cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) e2y); 72734cbdb2e17271b31d1511d2773edd241fafede7dztenghui eta1 = eta2; 72834cbdb2e17271b31d1511d2773edd241fafede7dztenghui e1x = e2x; 72934cbdb2e17271b31d1511d2773edd241fafede7dztenghui e1y = e2y; 73034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ep1x = ep2x; 73134cbdb2e17271b31d1511d2773edd241fafede7dztenghui ep1y = ep2y; 73234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 73334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 73434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 73534cbdb2e17271b31d1511d2773edd241fafede7dztenghui} 736