Path.kt revision 9c80550cbbe357a89e2abeeb9c7769fcaefc3a65
1245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy/* 2245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * Copyright (C) 2018 The Android Open Source Project 3245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * 4245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * Licensed under the Apache License, Version 2.0 (the "License"); 5245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * you may not use this file except in compliance with the License. 6245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * You may obtain a copy of the License at 7245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * 8245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * http://www.apache.org/licenses/LICENSE-2.0 9245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * 10245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * Unless required by applicable law or agreed to in writing, software 11245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * distributed under the License is distributed on an "AS IS" BASIS, 12245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * See the License for the specific language governing permissions and 14245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * limitations under the License. 15245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy */ 16245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy 174df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy@file:Suppress("NOTHING_TO_INLINE") 184df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy 199c80550cbbe357a89e2abeeb9c7769fcaefc3a65Jake Whartonpackage androidx.core.graphics 20245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy 21245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guyimport android.graphics.Path 22245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guyimport android.graphics.PointF 23245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guyimport android.support.annotation.RequiresApi 24245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy 25245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy/** 26245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * A [PathSegment] is a line segment that represents an approximate fraction 27245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * of a [Path] after [flattening][Path.flatten]. 28245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy */ 29245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guydata class PathSegment( 30af6781dcaa3ada9c67863a656fb591b56e7738ddJake Wharton /** The start point of the line segment. */ 31ec87f3d0ed8fb2a23ee2bbe8b323fdd7bdcbf4eaJake Wharton val start: PointF, 32af6781dcaa3ada9c67863a656fb591b56e7738ddJake Wharton /** Fraction along the length of the path that the start point resides. */ 33ec87f3d0ed8fb2a23ee2bbe8b323fdd7bdcbf4eaJake Wharton val startFraction: Float, 34af6781dcaa3ada9c67863a656fb591b56e7738ddJake Wharton /** The end point of the line segment. */ 35ec87f3d0ed8fb2a23ee2bbe8b323fdd7bdcbf4eaJake Wharton val end: PointF, 36af6781dcaa3ada9c67863a656fb591b56e7738ddJake Wharton /** Fraction along the length of the path that the end point resides. */ 37ec87f3d0ed8fb2a23ee2bbe8b323fdd7bdcbf4eaJake Wharton val endFraction: Float 38ec87f3d0ed8fb2a23ee2bbe8b323fdd7bdcbf4eaJake Wharton) 39245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy 40245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy/** 41245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * Flattens (or approximate) the [Path] with a series of line segments. 42245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * 43245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * @param error The acceptable error for a line on the Path. Typically this would be 44245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * 0.5 so that the error is less than half a pixel. This value must be 45245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * positive and is set to 0.5 by default. 46245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * 47245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy * @see Path.approximate 48245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy */ 49245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy@RequiresApi(26) 50245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guyfun Path.flatten(error: Float = 0.5f): Iterable<PathSegment> { 5104fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val pathData = approximate(error) 5204fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val pointCount = pathData.size / 3 5304fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val segments = ArrayList<PathSegment>(pointCount / 2) 5404fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy for (i in 1 until pointCount) { 55245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy val index = i * 3 56245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy val prevIndex = (i - 1) * 3 57245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy 5804fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val d = pathData[index] 5904fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val x = pathData[index + 1] 6004fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val y = pathData[index + 2] 61245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy 6204fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val pd = pathData[prevIndex] 6304fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val px = pathData[prevIndex + 1] 6404fa5880919c52cf71be74f085f9a5343f94d4d9Romain Guy val py = pathData[prevIndex + 2] 65245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy 66245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy if (d != pd && (x != py || y != py)) { 67245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy segments.add(PathSegment(PointF(px, py), pd, PointF(x, y), d)) 68245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy } 69245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy } 70245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy return segments 71245f352e7e7a8b37faebc5aefd52ce98ae9f08e6Romain Guy} 724df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy 734df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy/** 744df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy * Returns the union of two paths as a new [Path]. 754df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy */ 764df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy@RequiresApi(19) 774df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guyinline operator fun Path.plus(p: Path): Path { 784df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy return Path(this).apply { 794df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy op(p, Path.Op.UNION) 804df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy } 814df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy} 824df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy 834df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy/** 844df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy * Returns the difference of two paths as a new [Path]. 854df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy */ 864df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy@RequiresApi(19) 874df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guyinline operator fun Path.minus(p: Path): Path { 884df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy return Path(this).apply { 894df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy op(p, Path.Op.DIFFERENCE) 904df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy } 914df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy} 924df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy 934df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy/** 944df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy * Returns the union of two paths as a new [Path]. 954df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy */ 964df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy@RequiresApi(19) 974df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guyinline infix fun Path.and(p: Path) = this + p 984df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy 994df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy/** 1004df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy * Returns the intersection of two paths as a new [Path]. 1014df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy * If the paths do not intersect, returns an empty path. 1024df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy */ 1034df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy@RequiresApi(19) 1044df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guyinline infix fun Path.or(p: Path): Path { 1054df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy return Path().apply { 1064df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy op(this@or, p, Path.Op.INTERSECT) 1074df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy } 1084df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy} 1094df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy 1104df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy/** 1114df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy * Returns the union minus the intersection of two paths as a new [Path]. 1124df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy */ 1134df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy@RequiresApi(19) 1144df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guyinline infix fun Path.xor(p: Path): Path { 1154df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy return Path(this).apply { 1164df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy op(p, Path.Op.XOR) 1174df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy } 1184df8629cfe9f924b507ff26e53f896e025eb9ff7Romain Guy} 119