PathParser.java revision d85f3e0b931d3e127242a375e0d00c4696bba912
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. 2834cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 2934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Copies elements from {@code original} into a new array, from indexes start (inclusive) to 3034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * end (exclusive). The original order of elements is preserved. 3134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * If {@code end} is greater than {@code original.length}, the result is padded 3234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * with the value {@code 0.0f}. 3334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 3434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param original the original array 3534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param start the start index, inclusive 3634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param end the end index, exclusive 3734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return the new array 3834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length} 3934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @throws IllegalArgumentException if {@code start > end} 4034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @throws NullPointerException if {@code original == null} 4134cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 4234cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static float[] copyOfRange(float[] original, int start, int end) { 4334cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (start > end) { 4434cbdb2e17271b31d1511d2773edd241fafede7dztenghui throw new IllegalArgumentException(); 4534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 4634cbdb2e17271b31d1511d2773edd241fafede7dztenghui int originalLength = original.length; 4734cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (start < 0 || start > originalLength) { 4834cbdb2e17271b31d1511d2773edd241fafede7dztenghui throw new ArrayIndexOutOfBoundsException(); 4934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 5034cbdb2e17271b31d1511d2773edd241fafede7dztenghui int resultLength = end - start; 5134cbdb2e17271b31d1511d2773edd241fafede7dztenghui int copyLength = Math.min(resultLength, originalLength - start); 5234cbdb2e17271b31d1511d2773edd241fafede7dztenghui float[] result = new float[resultLength]; 5334cbdb2e17271b31d1511d2773edd241fafede7dztenghui System.arraycopy(original, start, result, 0, copyLength); 5434cbdb2e17271b31d1511d2773edd241fafede7dztenghui return result; 5534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 5634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 5734cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 5834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param pathData The string representing a path, the same as "d" string in svg file. 5934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return the generated Path object. 6034cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 6134cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static Path createPathFromPathData(String pathData) { 6234cbdb2e17271b31d1511d2773edd241fafede7dztenghui Path path = new Path(); 6334cbdb2e17271b31d1511d2773edd241fafede7dztenghui PathDataNode[] nodes = createNodesFromPathData(pathData); 6434cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (nodes != null) { 6534cbdb2e17271b31d1511d2773edd241fafede7dztenghui try { 6634cbdb2e17271b31d1511d2773edd241fafede7dztenghui PathDataNode.nodesToPath(nodes, path); 6734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } catch (RuntimeException e) { 6834cbdb2e17271b31d1511d2773edd241fafede7dztenghui throw new RuntimeException("Error in parsing " + pathData, e); 6934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 7034cbdb2e17271b31d1511d2773edd241fafede7dztenghui return path; 7134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 7234cbdb2e17271b31d1511d2773edd241fafede7dztenghui return null; 7334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 7434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 7534cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 7634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param pathData The string representing a path, the same as "d" string in svg file. 7734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return an array of the PathDataNode. 7834cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 7934cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static PathDataNode[] createNodesFromPathData(String pathData) { 8034cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (pathData == null) { 8134cbdb2e17271b31d1511d2773edd241fafede7dztenghui return null; 8234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 8334cbdb2e17271b31d1511d2773edd241fafede7dztenghui int start = 0; 8434cbdb2e17271b31d1511d2773edd241fafede7dztenghui int end = 1; 8534cbdb2e17271b31d1511d2773edd241fafede7dztenghui 8634cbdb2e17271b31d1511d2773edd241fafede7dztenghui ArrayList<PathDataNode> list = new ArrayList<PathDataNode>(); 8734cbdb2e17271b31d1511d2773edd241fafede7dztenghui while (end < pathData.length()) { 8834cbdb2e17271b31d1511d2773edd241fafede7dztenghui end = nextStart(pathData, end); 8934cbdb2e17271b31d1511d2773edd241fafede7dztenghui String s = pathData.substring(start, end).trim(); 9034cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (s.length() > 0) { 9134cbdb2e17271b31d1511d2773edd241fafede7dztenghui float[] val = getFloats(s); 9234cbdb2e17271b31d1511d2773edd241fafede7dztenghui addNode(list, s.charAt(0), val); 9334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 9434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 9534cbdb2e17271b31d1511d2773edd241fafede7dztenghui start = end; 9634cbdb2e17271b31d1511d2773edd241fafede7dztenghui end++; 9734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 9834cbdb2e17271b31d1511d2773edd241fafede7dztenghui if ((end - start) == 1 && start < pathData.length()) { 9934cbdb2e17271b31d1511d2773edd241fafede7dztenghui addNode(list, pathData.charAt(start), new float[0]); 10034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 10134cbdb2e17271b31d1511d2773edd241fafede7dztenghui return list.toArray(new PathDataNode[list.size()]); 10234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 10334cbdb2e17271b31d1511d2773edd241fafede7dztenghui 10434cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 10534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param source The array of PathDataNode to be duplicated. 10634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return a deep copy of the <code>source</code>. 10734cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 10834cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static PathDataNode[] deepCopyNodes(PathDataNode[] source) { 10934cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (source == null) { 11034cbdb2e17271b31d1511d2773edd241fafede7dztenghui return null; 11134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 11234cbdb2e17271b31d1511d2773edd241fafede7dztenghui PathDataNode[] copy = new PathParser.PathDataNode[source.length]; 11334cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int i = 0; i < source.length; i ++) { 11434cbdb2e17271b31d1511d2773edd241fafede7dztenghui copy[i] = new PathDataNode(source[i]); 11534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 11634cbdb2e17271b31d1511d2773edd241fafede7dztenghui return copy; 11734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 11834cbdb2e17271b31d1511d2773edd241fafede7dztenghui 11934cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 12034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param nodesFrom The source path represented in an array of PathDataNode 12134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param nodesTo The target path represented in an array of PathDataNode 12234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code> 12334cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 12434cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) { 12534cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (nodesFrom == null || nodesTo == null) { 12634cbdb2e17271b31d1511d2773edd241fafede7dztenghui return false; 12734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 12834cbdb2e17271b31d1511d2773edd241fafede7dztenghui 12934cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (nodesFrom.length != nodesTo.length) { 13034cbdb2e17271b31d1511d2773edd241fafede7dztenghui return false; 13134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 13234cbdb2e17271b31d1511d2773edd241fafede7dztenghui 13334cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int i = 0; i < nodesFrom.length; i ++) { 134d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu if (nodesFrom[i].type != nodesTo[i].type 135d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu || nodesFrom[i].params.length != nodesTo[i].params.length) { 13634cbdb2e17271b31d1511d2773edd241fafede7dztenghui return false; 13734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 13834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 13934cbdb2e17271b31d1511d2773edd241fafede7dztenghui return true; 14034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 14134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 14234cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 14334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Update the target's data to match the source. 14434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Before calling this, make sure canMorph(target, source) is true. 14534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 14634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param target The target path represented in an array of PathDataNode 14734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param source The source path represented in an array of PathDataNode 14834cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 14934cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static void updateNodes(PathDataNode[] target, PathDataNode[] source) { 15034cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int i = 0; i < source.length; i ++) { 151d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu target[i].type = source[i].type; 152d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu for (int j = 0; j < source[i].params.length; j ++) { 153d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu target[i].params[j] = source[i].params[j]; 15434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 15534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 15634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 15734cbdb2e17271b31d1511d2773edd241fafede7dztenghui 15834cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static int nextStart(String s, int end) { 15934cbdb2e17271b31d1511d2773edd241fafede7dztenghui char c; 16034cbdb2e17271b31d1511d2773edd241fafede7dztenghui 16134cbdb2e17271b31d1511d2773edd241fafede7dztenghui while (end < s.length()) { 16234cbdb2e17271b31d1511d2773edd241fafede7dztenghui c = s.charAt(end); 16334cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Note that 'e' or 'E' are not valid path commands, but could be 16434cbdb2e17271b31d1511d2773edd241fafede7dztenghui // used for floating point numbers' scientific notation. 16534cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Therefore, when searching for next command, we should ignore 'e' 16634cbdb2e17271b31d1511d2773edd241fafede7dztenghui // and 'E'. 16734cbdb2e17271b31d1511d2773edd241fafede7dztenghui if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0)) 16834cbdb2e17271b31d1511d2773edd241fafede7dztenghui && c != 'e' && c != 'E') { 16934cbdb2e17271b31d1511d2773edd241fafede7dztenghui return end; 17034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 17134cbdb2e17271b31d1511d2773edd241fafede7dztenghui end++; 17234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 17334cbdb2e17271b31d1511d2773edd241fafede7dztenghui return end; 17434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 17534cbdb2e17271b31d1511d2773edd241fafede7dztenghui 17634cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) { 17734cbdb2e17271b31d1511d2773edd241fafede7dztenghui list.add(new PathDataNode(cmd, val)); 17834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 17934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 18034cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static class ExtractFloatResult { 18134cbdb2e17271b31d1511d2773edd241fafede7dztenghui // We need to return the position of the next separator and whether the 18234cbdb2e17271b31d1511d2773edd241fafede7dztenghui // next float starts with a '-' or a '.'. 18334cbdb2e17271b31d1511d2773edd241fafede7dztenghui int mEndPosition; 18434cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean mEndWithNegOrDot; 18534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 18634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 18734cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 18834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Parse the floats in the string. 18934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * This is an optimized version of parseFloat(s.split(",|\\s")); 19034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 19134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param s the string containing a command and list of floats 19234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @return array of floats 19334cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 19434cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static float[] getFloats(String s) { 19534cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') { 19634cbdb2e17271b31d1511d2773edd241fafede7dztenghui return new float[0]; 19734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 19834cbdb2e17271b31d1511d2773edd241fafede7dztenghui try { 19934cbdb2e17271b31d1511d2773edd241fafede7dztenghui float[] results = new float[s.length()]; 20034cbdb2e17271b31d1511d2773edd241fafede7dztenghui int count = 0; 20134cbdb2e17271b31d1511d2773edd241fafede7dztenghui int startPosition = 1; 20234cbdb2e17271b31d1511d2773edd241fafede7dztenghui int endPosition = 0; 20334cbdb2e17271b31d1511d2773edd241fafede7dztenghui 20434cbdb2e17271b31d1511d2773edd241fafede7dztenghui ExtractFloatResult result = new ExtractFloatResult(); 20534cbdb2e17271b31d1511d2773edd241fafede7dztenghui int totalLength = s.length(); 20634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 20734cbdb2e17271b31d1511d2773edd241fafede7dztenghui // The startPosition should always be the first character of the 20834cbdb2e17271b31d1511d2773edd241fafede7dztenghui // current number, and endPosition is the character after the current 20934cbdb2e17271b31d1511d2773edd241fafede7dztenghui // number. 21034cbdb2e17271b31d1511d2773edd241fafede7dztenghui while (startPosition < totalLength) { 21134cbdb2e17271b31d1511d2773edd241fafede7dztenghui extract(s, startPosition, result); 21234cbdb2e17271b31d1511d2773edd241fafede7dztenghui endPosition = result.mEndPosition; 21334cbdb2e17271b31d1511d2773edd241fafede7dztenghui 21434cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (startPosition < endPosition) { 21534cbdb2e17271b31d1511d2773edd241fafede7dztenghui results[count++] = Float.parseFloat( 21634cbdb2e17271b31d1511d2773edd241fafede7dztenghui s.substring(startPosition, endPosition)); 21734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 21834cbdb2e17271b31d1511d2773edd241fafede7dztenghui 21934cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (result.mEndWithNegOrDot) { 22034cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Keep the '-' or '.' sign with next number. 22134cbdb2e17271b31d1511d2773edd241fafede7dztenghui startPosition = endPosition; 22234cbdb2e17271b31d1511d2773edd241fafede7dztenghui } else { 22334cbdb2e17271b31d1511d2773edd241fafede7dztenghui startPosition = endPosition + 1; 22434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 22534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 22634cbdb2e17271b31d1511d2773edd241fafede7dztenghui return copyOfRange(results, 0, count); 22734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } catch (NumberFormatException e) { 22834cbdb2e17271b31d1511d2773edd241fafede7dztenghui throw new RuntimeException("error in parsing \"" + s + "\"", e); 22934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 23034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 23134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 23234cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 23334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Calculate the position of the next comma or space or negative sign 23434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param s the string to search 23534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param start the position to start searching 23634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param result the result of the extraction, including the position of the 23734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * the starting position of next number, whether it is ending with a '-'. 23834cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 23934cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void extract(String s, int start, ExtractFloatResult result) { 24034cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Now looking for ' ', ',', '.' or '-' from the start. 24134cbdb2e17271b31d1511d2773edd241fafede7dztenghui int currentIndex = start; 24234cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean foundSeparator = false; 24334cbdb2e17271b31d1511d2773edd241fafede7dztenghui result.mEndWithNegOrDot = false; 24434cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean secondDot = false; 24534cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean isExponential = false; 24634cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (; currentIndex < s.length(); currentIndex++) { 24734cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean isPrevExponential = isExponential; 24834cbdb2e17271b31d1511d2773edd241fafede7dztenghui isExponential = false; 24934cbdb2e17271b31d1511d2773edd241fafede7dztenghui char currentChar = s.charAt(currentIndex); 25034cbdb2e17271b31d1511d2773edd241fafede7dztenghui switch (currentChar) { 25134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case ' ': 25234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case ',': 25334cbdb2e17271b31d1511d2773edd241fafede7dztenghui foundSeparator = true; 25434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 25534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case '-': 25634cbdb2e17271b31d1511d2773edd241fafede7dztenghui // The negative sign following a 'e' or 'E' is not a separator. 25734cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (currentIndex != start && !isPrevExponential) { 25834cbdb2e17271b31d1511d2773edd241fafede7dztenghui foundSeparator = true; 25934cbdb2e17271b31d1511d2773edd241fafede7dztenghui result.mEndWithNegOrDot = true; 26034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 26134cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 26234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case '.': 26334cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (!secondDot) { 26434cbdb2e17271b31d1511d2773edd241fafede7dztenghui secondDot = true; 26534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } else { 26634cbdb2e17271b31d1511d2773edd241fafede7dztenghui // This is the second dot, and it is considered as a separator. 26734cbdb2e17271b31d1511d2773edd241fafede7dztenghui foundSeparator = true; 26834cbdb2e17271b31d1511d2773edd241fafede7dztenghui result.mEndWithNegOrDot = true; 26934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 27034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 27134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'e': 27234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'E': 27334cbdb2e17271b31d1511d2773edd241fafede7dztenghui isExponential = true; 27434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 27534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 27634cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (foundSeparator) { 27734cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 27834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 27934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 28034cbdb2e17271b31d1511d2773edd241fafede7dztenghui // When there is nothing found, then we put the end position to the end 28134cbdb2e17271b31d1511d2773edd241fafede7dztenghui // of the string. 28234cbdb2e17271b31d1511d2773edd241fafede7dztenghui result.mEndPosition = currentIndex; 28334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 28434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 28534cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 28634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Each PathDataNode represents one command in the "d" attribute of the svg 28734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * file. 28834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * An array of PathDataNode can represent the whole "d" attribute. 28934cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 29034cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static class PathDataNode { 291d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu /*package*/ 292d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu char type; 293d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu float[] params; 29434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 29534cbdb2e17271b31d1511d2773edd241fafede7dztenghui private PathDataNode(char type, float[] params) { 296d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu this.type = type; 297d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu this.params = params; 29834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 29934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 30034cbdb2e17271b31d1511d2773edd241fafede7dztenghui private PathDataNode(PathDataNode n) { 301d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu type = n.type; 302d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu params = copyOfRange(n.params, 0, n.params.length); 30334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 30434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 30534cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 30634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Convert an array of PathDataNode to Path. 30734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 30834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param node The source array of PathDataNode. 30934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param path The target Path object. 31034cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 31134cbdb2e17271b31d1511d2773edd241fafede7dztenghui public static void nodesToPath(PathDataNode[] node, Path path) { 31234cbdb2e17271b31d1511d2773edd241fafede7dztenghui float[] current = new float[6]; 31334cbdb2e17271b31d1511d2773edd241fafede7dztenghui char previousCommand = 'm'; 31434cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int i = 0; i < node.length; i++) { 315d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu addCommand(path, current, previousCommand, node[i].type, node[i].params); 316d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu previousCommand = node[i].type; 31734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 31834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 31934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 32034cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 32134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * The current PathDataNode will be interpolated between the 32234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * <code>nodeFrom</code> and <code>nodeTo</code> according to the 32334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * <code>fraction</code>. 32434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 32534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param nodeFrom The start value as a PathDataNode. 32634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param nodeTo The end value as a PathDataNode 32734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param fraction The fraction to interpolate. 32834cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 32934cbdb2e17271b31d1511d2773edd241fafede7dztenghui public void interpolatePathDataNode(PathDataNode nodeFrom, 33034cbdb2e17271b31d1511d2773edd241fafede7dztenghui PathDataNode nodeTo, float fraction) { 331d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu for (int i = 0; i < nodeFrom.params.length; i++) { 332d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu params[i] = nodeFrom.params[i] * (1 - fraction) 333d85f3e0b931d3e127242a375e0d00c4696bba912Teng-Hui Zhu + nodeTo.params[i] * fraction; 33434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 33534cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 33634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 33734cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void addCommand(Path path, float[] current, 33834cbdb2e17271b31d1511d2773edd241fafede7dztenghui char previousCmd, char cmd, float[] val) { 33934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 34034cbdb2e17271b31d1511d2773edd241fafede7dztenghui int incr = 2; 34134cbdb2e17271b31d1511d2773edd241fafede7dztenghui float currentX = current[0]; 34234cbdb2e17271b31d1511d2773edd241fafede7dztenghui float currentY = current[1]; 34334cbdb2e17271b31d1511d2773edd241fafede7dztenghui float ctrlPointX = current[2]; 34434cbdb2e17271b31d1511d2773edd241fafede7dztenghui float ctrlPointY = current[3]; 34534cbdb2e17271b31d1511d2773edd241fafede7dztenghui float currentSegmentStartX = current[4]; 34634cbdb2e17271b31d1511d2773edd241fafede7dztenghui float currentSegmentStartY = current[5]; 34734cbdb2e17271b31d1511d2773edd241fafede7dztenghui float reflectiveCtrlPointX; 34834cbdb2e17271b31d1511d2773edd241fafede7dztenghui float reflectiveCtrlPointY; 34934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 35034cbdb2e17271b31d1511d2773edd241fafede7dztenghui switch (cmd) { 35134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'z': 35234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'Z': 35334cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.close(); 35434cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Path is closed here, but we need to move the pen to the 35534cbdb2e17271b31d1511d2773edd241fafede7dztenghui // closed position. So we cache the segment's starting position, 35634cbdb2e17271b31d1511d2773edd241fafede7dztenghui // and restore it here. 35734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = currentSegmentStartX; 35834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = currentSegmentStartY; 35934cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentSegmentStartX; 36034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentSegmentStartY; 36134cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.moveTo(currentX, currentY); 36234cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 36334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'm': 36434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'M': 36534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'l': 36634cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'L': 36734cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 't': 36834cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'T': 36934cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 2; 37034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 37134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'h': 37234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'H': 37334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'v': 37434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'V': 37534cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 1; 37634cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 37734cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'c': 37834cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'C': 37934cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 6; 38034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 38134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 's': 38234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'S': 38334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'q': 38434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'Q': 38534cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 4; 38634cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 38734cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'a': 38834cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'A': 38934cbdb2e17271b31d1511d2773edd241fafede7dztenghui incr = 7; 39034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 39134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 39234cbdb2e17271b31d1511d2773edd241fafede7dztenghui 39334cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int k = 0; k < val.length; k += incr) { 39434cbdb2e17271b31d1511d2773edd241fafede7dztenghui switch (cmd) { 39534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'm': // moveto - Start a new sub-path (relative) 39634cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rMoveTo(val[k + 0], val[k + 1]); 39734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 0]; 39834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 1]; 39934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentSegmentStartX = currentX; 40034cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentSegmentStartY = currentY; 40134cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 40234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'M': // moveto - Start a new sub-path 40334cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.moveTo(val[k + 0], val[k + 1]); 40434cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 0]; 40534cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 1]; 40634cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentSegmentStartX = currentX; 40734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentSegmentStartY = currentY; 40834cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 40934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'l': // lineto - Draw a line from the current point (relative) 41034cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rLineTo(val[k + 0], val[k + 1]); 41134cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 0]; 41234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 1]; 41334cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 41434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'L': // lineto - Draw a line from the current point 41534cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.lineTo(val[k + 0], val[k + 1]); 41634cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 0]; 41734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 1]; 41834cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 41934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'h': // horizontal lineto - Draws a horizontal line (relative) 42034cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rLineTo(val[k + 0], 0); 42134cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 0]; 42234cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 42334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'H': // horizontal lineto - Draws a horizontal line 42434cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.lineTo(val[k + 0], currentY); 42534cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 0]; 42634cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 42734cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'v': // vertical lineto - Draws a vertical line from the current point (r) 42834cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rLineTo(0, val[k + 0]); 42934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 0]; 43034cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 43134cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'V': // vertical lineto - Draws a vertical line from the current point 43234cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.lineTo(currentX, val[k + 0]); 43334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 0]; 43434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 43534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'c': // curveto - Draws a cubic Bézier curve (relative) 43634cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], 43734cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 4], val[k + 5]); 43834cbdb2e17271b31d1511d2773edd241fafede7dztenghui 43934cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX + val[k + 2]; 44034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY + val[k + 3]; 44134cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 4]; 44234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 5]; 44334cbdb2e17271b31d1511d2773edd241fafede7dztenghui 44434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 44534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'C': // curveto - Draws a cubic Bézier curve 44634cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], 44734cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 4], val[k + 5]); 44834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 4]; 44934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 5]; 45034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = val[k + 2]; 45134cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = val[k + 3]; 45234cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 45334cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp) 45434cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = 0; 45534cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = 0; 45634cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (previousCmd == 'c' || previousCmd == 's' 45734cbdb2e17271b31d1511d2773edd241fafede7dztenghui || previousCmd == 'C' || previousCmd == 'S') { 45834cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = currentX - ctrlPointX; 45934cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = currentY - ctrlPointY; 46034cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 46134cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, 46234cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], val[k + 1], 46334cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 2], val[k + 3]); 46434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 46534cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX + val[k + 0]; 46634cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY + val[k + 1]; 46734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 2]; 46834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 3]; 46934cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 47034cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp) 47134cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = currentX; 47234cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = currentY; 47334cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (previousCmd == 'c' || previousCmd == 's' 47434cbdb2e17271b31d1511d2773edd241fafede7dztenghui || previousCmd == 'C' || previousCmd == 'S') { 47534cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = 2 * currentX - ctrlPointX; 47634cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = 2 * currentY - ctrlPointY; 47734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 47834cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, 47934cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], val[k + 1], val[k + 2], val[k + 3]); 48034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = val[k + 0]; 48134cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = val[k + 1]; 48234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 2]; 48334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 3]; 48434cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 48534cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'q': // Draws a quadratic Bézier (relative) 48634cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); 48734cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX + val[k + 0]; 48834cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY + val[k + 1]; 48934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 2]; 49034cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 3]; 49134cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 49234cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'Q': // Draws a quadratic Bézier 49334cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); 49434cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = val[k + 0]; 49534cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = val[k + 1]; 49634cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 2]; 49734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 3]; 49834cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 49934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 't': // Draws a quadratic Bézier curve(reflective control point)(relative) 50034cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = 0; 50134cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = 0; 50234cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (previousCmd == 'q' || previousCmd == 't' 50334cbdb2e17271b31d1511d2773edd241fafede7dztenghui || previousCmd == 'Q' || previousCmd == 'T') { 50434cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = currentX - ctrlPointX; 50534cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = currentY - ctrlPointY; 50634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 50734cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, 50834cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], val[k + 1]); 50934cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX + reflectiveCtrlPointX; 51034cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY + reflectiveCtrlPointY; 51134cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 0]; 51234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 1]; 51334cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 51434cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'T': // Draws a quadratic Bézier curve (reflective control point) 51534cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = currentX; 51634cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = currentY; 51734cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (previousCmd == 'q' || previousCmd == 't' 51834cbdb2e17271b31d1511d2773edd241fafede7dztenghui || previousCmd == 'Q' || previousCmd == 'T') { 51934cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointX = 2 * currentX - ctrlPointX; 52034cbdb2e17271b31d1511d2773edd241fafede7dztenghui reflectiveCtrlPointY = 2 * currentY - ctrlPointY; 52134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 52234cbdb2e17271b31d1511d2773edd241fafede7dztenghui path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, 52334cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], val[k + 1]); 52434cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = reflectiveCtrlPointX; 52534cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = reflectiveCtrlPointY; 52634cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX = val[k + 0]; 52734cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY = val[k + 1]; 52834cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 52934cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'a': // Draws an elliptical arc 53034cbdb2e17271b31d1511d2773edd241fafede7dztenghui // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) 53134cbdb2e17271b31d1511d2773edd241fafede7dztenghui drawArc(path, 53234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX, 53334cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY, 53434cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 5] + currentX, 53534cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 6] + currentY, 53634cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 0], 53734cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 1], 53834cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 2], 53934cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 3] != 0, 54034cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 4] != 0); 54134cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX += val[k + 5]; 54234cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY += val[k + 6]; 54334cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointX = currentX; 54434cbdb2e17271b31d1511d2773edd241fafede7dztenghui ctrlPointY = currentY; 54534cbdb2e17271b31d1511d2773edd241fafede7dztenghui break; 54634cbdb2e17271b31d1511d2773edd241fafede7dztenghui case 'A': // Draws an elliptical arc 54734cbdb2e17271b31d1511d2773edd241fafede7dztenghui drawArc(path, 54834cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentX, 54934cbdb2e17271b31d1511d2773edd241fafede7dztenghui currentY, 55034cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 5], 55134cbdb2e17271b31d1511d2773edd241fafede7dztenghui val[k + 6], 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 } 56334cbdb2e17271b31d1511d2773edd241fafede7dztenghui previousCmd = cmd; 56434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 56534cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[0] = currentX; 56634cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[1] = currentY; 56734cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[2] = ctrlPointX; 56834cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[3] = ctrlPointY; 56934cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[4] = currentSegmentStartX; 57034cbdb2e17271b31d1511d2773edd241fafede7dztenghui current[5] = currentSegmentStartY; 57134cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 57234cbdb2e17271b31d1511d2773edd241fafede7dztenghui 57334cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void drawArc(Path p, 57434cbdb2e17271b31d1511d2773edd241fafede7dztenghui float x0, 57534cbdb2e17271b31d1511d2773edd241fafede7dztenghui float y0, 57634cbdb2e17271b31d1511d2773edd241fafede7dztenghui float x1, 57734cbdb2e17271b31d1511d2773edd241fafede7dztenghui float y1, 57834cbdb2e17271b31d1511d2773edd241fafede7dztenghui float a, 57934cbdb2e17271b31d1511d2773edd241fafede7dztenghui float b, 58034cbdb2e17271b31d1511d2773edd241fafede7dztenghui float theta, 58134cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean isMoreThanHalf, 58234cbdb2e17271b31d1511d2773edd241fafede7dztenghui boolean isPositiveArc) { 58334cbdb2e17271b31d1511d2773edd241fafede7dztenghui 58434cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Convert rotation angle from degrees to radians */ 58534cbdb2e17271b31d1511d2773edd241fafede7dztenghui double thetaD = Math.toRadians(theta); 58634cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Pre-compute rotation matrix entries */ 58734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cosTheta = Math.cos(thetaD); 58834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sinTheta = Math.sin(thetaD); 58934cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Transform (x0, y0) and (x1, y1) into unit space */ 59034cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* using (inverse) rotation, followed by (inverse) scale */ 59134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double x0p = (x0 * cosTheta + y0 * sinTheta) / a; 59234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double y0p = (-x0 * sinTheta + y0 * cosTheta) / b; 59334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double x1p = (x1 * cosTheta + y1 * sinTheta) / a; 59434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double y1p = (-x1 * sinTheta + y1 * cosTheta) / b; 59534cbdb2e17271b31d1511d2773edd241fafede7dztenghui 59634cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Compute differences and averages */ 59734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double dx = x0p - x1p; 59834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double dy = y0p - y1p; 59934cbdb2e17271b31d1511d2773edd241fafede7dztenghui double xm = (x0p + x1p) / 2; 60034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ym = (y0p + y1p) / 2; 60134cbdb2e17271b31d1511d2773edd241fafede7dztenghui /* Solve for intersecting unit circles */ 60234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double dsq = dx * dx + dy * dy; 60334cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (dsq == 0.0) { 60434cbdb2e17271b31d1511d2773edd241fafede7dztenghui Log.w(LOGTAG, " Points are coincident"); 60534cbdb2e17271b31d1511d2773edd241fafede7dztenghui return; /* Points are coincident */ 60634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 60734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double disc = 1.0 / dsq - 1.0 / 4.0; 60834cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (disc < 0.0) { 60934cbdb2e17271b31d1511d2773edd241fafede7dztenghui Log.w(LOGTAG, "Points are too far apart " + dsq); 61034cbdb2e17271b31d1511d2773edd241fafede7dztenghui float adjust = (float) (Math.sqrt(dsq) / 1.99999); 61134cbdb2e17271b31d1511d2773edd241fafede7dztenghui drawArc(p, x0, y0, x1, y1, a * adjust, 61234cbdb2e17271b31d1511d2773edd241fafede7dztenghui b * adjust, theta, isMoreThanHalf, isPositiveArc); 61334cbdb2e17271b31d1511d2773edd241fafede7dztenghui return; /* Points are too far apart */ 61434cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 61534cbdb2e17271b31d1511d2773edd241fafede7dztenghui double s = Math.sqrt(disc); 61634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sdx = s * dx; 61734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sdy = s * dy; 61834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cx; 61934cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cy; 62034cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (isMoreThanHalf == isPositiveArc) { 62134cbdb2e17271b31d1511d2773edd241fafede7dztenghui cx = xm - sdy; 62234cbdb2e17271b31d1511d2773edd241fafede7dztenghui cy = ym + sdx; 62334cbdb2e17271b31d1511d2773edd241fafede7dztenghui } else { 62434cbdb2e17271b31d1511d2773edd241fafede7dztenghui cx = xm + sdy; 62534cbdb2e17271b31d1511d2773edd241fafede7dztenghui cy = ym - sdx; 62634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 62734cbdb2e17271b31d1511d2773edd241fafede7dztenghui 62834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double eta0 = Math.atan2((y0p - cy), (x0p - cx)); 62934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 63034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double eta1 = Math.atan2((y1p - cy), (x1p - cx)); 63134cbdb2e17271b31d1511d2773edd241fafede7dztenghui 63234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sweep = (eta1 - eta0); 63334cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (isPositiveArc != (sweep >= 0)) { 63434cbdb2e17271b31d1511d2773edd241fafede7dztenghui if (sweep > 0) { 63534cbdb2e17271b31d1511d2773edd241fafede7dztenghui sweep -= 2 * Math.PI; 63634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } else { 63734cbdb2e17271b31d1511d2773edd241fafede7dztenghui sweep += 2 * Math.PI; 63834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 63934cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 64034cbdb2e17271b31d1511d2773edd241fafede7dztenghui 64134cbdb2e17271b31d1511d2773edd241fafede7dztenghui cx *= a; 64234cbdb2e17271b31d1511d2773edd241fafede7dztenghui cy *= b; 64334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double tcx = cx; 64434cbdb2e17271b31d1511d2773edd241fafede7dztenghui cx = cx * cosTheta - cy * sinTheta; 64534cbdb2e17271b31d1511d2773edd241fafede7dztenghui cy = tcx * sinTheta + cy * cosTheta; 64634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 64734cbdb2e17271b31d1511d2773edd241fafede7dztenghui arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep); 64834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 64934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 65034cbdb2e17271b31d1511d2773edd241fafede7dztenghui /** 65134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * Converts an arc to cubic Bezier segments and records them in p. 65234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * 65334cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param p The target for the cubic Bezier segments 65434cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param cx The x coordinate center of the ellipse 65534cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param cy The y coordinate center of the ellipse 65634cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param a The radius of the ellipse in the horizontal direction 65734cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param b The radius of the ellipse in the vertical direction 65834cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param e1x E(eta1) x coordinate of the starting point of the arc 65934cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param e1y E(eta2) y coordinate of the starting point of the arc 66034cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane 66134cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param start The start angle of the arc on the ellipse 66234cbdb2e17271b31d1511d2773edd241fafede7dztenghui * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse 66334cbdb2e17271b31d1511d2773edd241fafede7dztenghui */ 66434cbdb2e17271b31d1511d2773edd241fafede7dztenghui private static void arcToBezier(Path p, 66534cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cx, 66634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cy, 66734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double a, 66834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double b, 66934cbdb2e17271b31d1511d2773edd241fafede7dztenghui double e1x, 67034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double e1y, 67134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double theta, 67234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double start, 67334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sweep) { 67434cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html 67534cbdb2e17271b31d1511d2773edd241fafede7dztenghui // and http://www.spaceroots.org/documents/ellipse/node22.html 67634cbdb2e17271b31d1511d2773edd241fafede7dztenghui 67734cbdb2e17271b31d1511d2773edd241fafede7dztenghui // Maximum of 45 degrees per cubic Bezier segment 67834cbdb2e17271b31d1511d2773edd241fafede7dztenghui int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI)); 67934cbdb2e17271b31d1511d2773edd241fafede7dztenghui 68034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double eta1 = start; 68134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cosTheta = Math.cos(theta); 68234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sinTheta = Math.sin(theta); 68334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cosEta1 = Math.cos(eta1); 68434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sinEta1 = Math.sin(eta1); 68534cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1); 68634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1); 68734cbdb2e17271b31d1511d2773edd241fafede7dztenghui 68834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double anglePerSegment = sweep / numSegments; 68934cbdb2e17271b31d1511d2773edd241fafede7dztenghui for (int i = 0; i < numSegments; i++) { 69034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double eta2 = eta1 + anglePerSegment; 69134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double sinEta2 = Math.sin(eta2); 69234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double cosEta2 = Math.cos(eta2); 69334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2); 69434cbdb2e17271b31d1511d2773edd241fafede7dztenghui double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2); 69534cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2; 69634cbdb2e17271b31d1511d2773edd241fafede7dztenghui double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2; 69734cbdb2e17271b31d1511d2773edd241fafede7dztenghui double tanDiff2 = Math.tan((eta2 - eta1) / 2); 69834cbdb2e17271b31d1511d2773edd241fafede7dztenghui double alpha = 69934cbdb2e17271b31d1511d2773edd241fafede7dztenghui Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3; 70034cbdb2e17271b31d1511d2773edd241fafede7dztenghui double q1x = e1x + alpha * ep1x; 70134cbdb2e17271b31d1511d2773edd241fafede7dztenghui double q1y = e1y + alpha * ep1y; 70234cbdb2e17271b31d1511d2773edd241fafede7dztenghui double q2x = e2x - alpha * ep2x; 70334cbdb2e17271b31d1511d2773edd241fafede7dztenghui double q2y = e2y - alpha * ep2y; 70434cbdb2e17271b31d1511d2773edd241fafede7dztenghui 70534cbdb2e17271b31d1511d2773edd241fafede7dztenghui p.cubicTo((float) q1x, 70634cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) q1y, 70734cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) q2x, 70834cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) q2y, 70934cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) e2x, 71034cbdb2e17271b31d1511d2773edd241fafede7dztenghui (float) e2y); 71134cbdb2e17271b31d1511d2773edd241fafede7dztenghui eta1 = eta2; 71234cbdb2e17271b31d1511d2773edd241fafede7dztenghui e1x = e2x; 71334cbdb2e17271b31d1511d2773edd241fafede7dztenghui e1y = e2y; 71434cbdb2e17271b31d1511d2773edd241fafede7dztenghui ep1x = ep2x; 71534cbdb2e17271b31d1511d2773edd241fafede7dztenghui ep1y = ep2y; 71634cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 71734cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 71834cbdb2e17271b31d1511d2773edd241fafede7dztenghui } 71934cbdb2e17271b31d1511d2773edd241fafede7dztenghui} 720