1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.compose.ui.graphics.vector 18 19 /** [PathBuilder] provides a fluent API to creates a list of [PathNode], used to describe a path. */ 20 class PathBuilder { 21 // 88% of Material icons use 32 or fewer path nodes 22 private val _nodes = ArrayList<PathNode>(32) 23 24 /** Returns the list of [PathNode] currently held in this builder. */ 25 val nodes: List<PathNode> 26 get() = _nodes 27 28 /** Closes the current contour by adding a [PathNode.Close] to [nodes]. */ closenull29 fun close(): PathBuilder { 30 _nodes.add(PathNode.Close) 31 return this 32 } 33 34 /** 35 * Start a new contour at position ([x], [y]) by adding a [PathNode.MoveTo] to [nodes]. 36 * 37 * @param x The x coordinate of the start of the new contour 38 * @param y The y coordinate of the start of the new contour 39 */ moveTonull40 fun moveTo(x: Float, y: Float): PathBuilder { 41 _nodes.add(PathNode.MoveTo(x, y)) 42 return this 43 } 44 45 /** 46 * Start a new contour at the offset ([dx], [dy]) relative to the last path position by adding a 47 * [PathNode.RelativeMoveTo] to [nodes]. 48 * 49 * @param dx The x offset of the start of the new contour, relative to the last path position 50 * @param dy The y offset of the start of the new contour, relative to the last path position 51 */ moveToRelativenull52 fun moveToRelative(dx: Float, dy: Float): PathBuilder { 53 _nodes.add(PathNode.RelativeMoveTo(dx, dy)) 54 return this 55 } 56 57 /** 58 * Add a line from the last point to the position ([x], [y]) by adding a [PathNode.LineTo] to 59 * [nodes]. If no contour has been created by calling [moveTo] first, the origin of the line is 60 * set to (0, 0). 61 * 62 * @param x The x coordinate of the end of the line 63 * @param y The y coordinate of the end of the line 64 */ lineTonull65 fun lineTo(x: Float, y: Float): PathBuilder { 66 _nodes.add(PathNode.LineTo(x, y)) 67 return this 68 } 69 70 /** 71 * Add a line from the last point to the offset ([dx], [dy]) relative to the last point by 72 * adding a [PathNode.RelativeLineTo] to [nodes]. If no contour has been created by calling 73 * [moveTo] first, the origin of the line is set to (0, 0). 74 * 75 * @param dx The x offset of the end of the line, relative to the last path position 76 * @param dy The y offset of the end of the line, relative to the last path position 77 */ lineToRelativenull78 fun lineToRelative(dx: Float, dy: Float): PathBuilder { 79 _nodes.add(PathNode.RelativeLineTo(dx, dy)) 80 return this 81 } 82 83 /** 84 * Add a line from the last point to the position ([x], `oy`), where `oy` is the y coordinate of 85 * the last point, by adding a [PathNode.HorizontalTo] to [nodes]. If no contour has been 86 * created by calling [moveTo] first, the origin of the line is set to (0, 0). 87 * 88 * @param x The x coordinate of the end of the line 89 */ horizontalLineTonull90 fun horizontalLineTo(x: Float): PathBuilder { 91 _nodes.add(PathNode.HorizontalTo(x)) 92 return this 93 } 94 95 /** 96 * Add a line from the last point to the position ([dx] `+ ox`, `oy`), where `ox` and `oy` are 97 * the x and y coordinates of the last point, by adding a [PathNode.RelativeHorizontalTo] to 98 * [nodes]. If no contour has been created by calling [moveTo] first, the origin of the line is 99 * set to (0, 0). 100 * 101 * @param dx The x offset of the end of the line, relative to the last path position 102 */ horizontalLineToRelativenull103 fun horizontalLineToRelative(dx: Float): PathBuilder { 104 _nodes.add(PathNode.RelativeHorizontalTo(dx)) 105 return this 106 } 107 108 /** 109 * Add a line from the last point to the position (`ox`, [y]), where `ox` is the x coordinate of 110 * the last point, by adding a [PathNode.VerticalTo] to [nodes]. If no contour has been created 111 * by calling [moveTo] first, the origin of the line is set to (0, 0). 112 * 113 * @param y The y coordinate of the end of the line 114 */ verticalLineTonull115 fun verticalLineTo(y: Float): PathBuilder { 116 _nodes.add(PathNode.VerticalTo(y)) 117 return this 118 } 119 120 /** 121 * Add a line from the last point to the position (`ox`, [dy] `+ oy`), where `ox` and `oy` are 122 * the x and y coordinates of the last point, by adding a [PathNode.RelativeVerticalTo] to 123 * [nodes]. If no contour has been created by calling [moveTo] first, the origin of the line is 124 * set to (0, 0). 125 * 126 * @param dy The y offset of the end of the line, relative to the last path position 127 */ verticalLineToRelativenull128 fun verticalLineToRelative(dy: Float): PathBuilder { 129 _nodes.add(PathNode.RelativeVerticalTo(dy)) 130 return this 131 } 132 133 /** 134 * Add a cubic Bézier from the last point to the position ([x3], [y3]), approaching the control 135 * points ([x1], [y1]) and ([x2], [y2]), by adding a [PathNode.CurveTo] to [nodes]. If no 136 * contour has been created by calling [moveTo] first, the origin of the curve is set to (0, 0). 137 * 138 * @param x1 The x coordinate of the first control point of the cubic curve 139 * @param y1 The y coordinate of the first control point of the cubic curve 140 * @param x2 The x coordinate of the second control point of the cubic curve 141 * @param y2 The y coordinate of the second control point of the cubic curve 142 * @param x3 The x coordinate of the end point of the cubic curve 143 * @param y3 The y coordinate of the end point of the cubic curve 144 */ curveTonull145 fun curveTo(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float): PathBuilder { 146 _nodes.add(PathNode.CurveTo(x1, y1, x2, y2, x3, y3)) 147 return this 148 } 149 150 /** 151 * Add a cubic Bézier by adding a [PathNode.CurveTo] to [nodes]. If no contour has been created 152 * by calling [moveTo] first, the origin of the curve is set to (0, 0). The cubic Bézier control 153 * and end points are defined by offsets relative to the last point. 154 * 155 * @param dx1 The x offset of the first control point of the cubic curve, relative to the last 156 * path position 157 * @param dy1 The y offset of the first control point of the cubic curve, relative to the last 158 * path position 159 * @param dx2 The x offset of the second control point of the cubic curve, relative to the last 160 * path position 161 * @param dy2 The y offset of the second control point of the cubic curve, relative to the last 162 * path position 163 * @param dx3 The x offset of the end point of the cubic curve, relative to the last path 164 * position 165 * @param dy3 The y offset of the end point of the cubic curve, relative to the last path 166 * position 167 */ curveToRelativenull168 fun curveToRelative( 169 dx1: Float, 170 dy1: Float, 171 dx2: Float, 172 dy2: Float, 173 dx3: Float, 174 dy3: Float 175 ): PathBuilder { 176 _nodes.add(PathNode.RelativeCurveTo(dx1, dy1, dx2, dy2, dx3, dy3)) 177 return this 178 } 179 180 /** 181 * Add a cubic Bézier from the last point to the position ([x2], [y2]). The first control point 182 * is the reflection of the second control point of the previous command. If there is no 183 * previous command or the previous command is not a cubic Bézier, the first control point is 184 * set to the last path position. The second control point is defined by ([x1], [y1]). Calling 185 * this method adds a [PathNode.ReflectiveCurveTo] to [nodes]. If no contour has been created by 186 * calling [moveTo] first, the origin of the curve is set to (0, 0). 187 * 188 * @param x1 The x coordinate of the second control point of the cubic curve 189 * @param y1 The y coordinate of the second control point of the cubic curve 190 * @param x2 The x coordinate of the end point of the cubic curve 191 * @param y2 The y coordinate of the end point of the cubic curve 192 */ reflectiveCurveTonull193 fun reflectiveCurveTo(x1: Float, y1: Float, x2: Float, y2: Float): PathBuilder { 194 _nodes.add(PathNode.ReflectiveCurveTo(x1, y1, x2, y2)) 195 return this 196 } 197 198 /** 199 * Add a cubic Bézier by adding a [PathNode.RelativeReflectiveCurveTo] to [nodes]. If no contour 200 * has been created by calling [moveTo] first, the origin of the curve is set to (0, 0). The 201 * cubic Bézier second control point and end points are defined by offsets relative to the last 202 * point. The reflective nature of the curve is described in [reflectiveCurveTo]. 203 * 204 * @param dx1 The x offset of the second control point of the cubic curve, relative to the last 205 * path position 206 * @param dy1 The y offset of the second control point of the cubic curve, relative to the last 207 * path position 208 * @param dx2 The x offset of the end point of the cubic curve, relative to the last path 209 * position 210 * @param dy2 The y offset of the end point of the cubic curve, relative to the last path 211 * position 212 */ reflectiveCurveToRelativenull213 fun reflectiveCurveToRelative(dx1: Float, dy1: Float, dx2: Float, dy2: Float): PathBuilder { 214 _nodes.add(PathNode.RelativeReflectiveCurveTo(dx1, dy1, dx2, dy2)) 215 return this 216 } 217 218 /** 219 * Add a quadratic Bézier from the last point to the position ([x2], [y2]), approaching the 220 * control point ([x1], [y1]), by adding a [PathNode.QuadTo] to [nodes]. If no contour has been 221 * created by calling [moveTo] first, the origin of the curve is set to (0, 0). 222 * 223 * @param x1 The x coordinate of the control point of the quadratic curve 224 * @param y1 The y coordinate of the control point of the quadratic curve 225 * @param x2 The x coordinate of the end point of the quadratic curve 226 * @param y2 The y coordinate of the end point of the quadratic curve 227 */ quadTonull228 fun quadTo(x1: Float, y1: Float, x2: Float, y2: Float): PathBuilder { 229 _nodes.add(PathNode.QuadTo(x1, y1, x2, y2)) 230 return this 231 } 232 233 /** 234 * Add a quadratic Bézier by adding a [PathNode.RelativeQuadTo] to [nodes]. If no contour has 235 * been created by calling [moveTo] first, the origin of the curve is set to (0, 0). The control 236 * point and end point of the curve are defined by offsets relative to the last point. 237 * 238 * @param dx1 The x offset of the control point of the quadratic curve, relative to the last 239 * path position 240 * @param dy1 The y offset of the control point of the quadratic curve, relative to the last 241 * path position 242 * @param dx2 The x offset of the end point of the quadratic curve, relative to the last path 243 * position 244 * @param dy2 The y offset of the end point of the quadratic curve, relative to the last path 245 * position 246 */ quadToRelativenull247 fun quadToRelative(dx1: Float, dy1: Float, dx2: Float, dy2: Float): PathBuilder { 248 _nodes.add(PathNode.RelativeQuadTo(dx1, dy1, dx2, dy2)) 249 return this 250 } 251 252 /** 253 * Add a quadratic Bézier from the last point to the position ([x1], [y1]). The control point is 254 * the reflection of the control point of the previous command. If there is no previous command 255 * or the previous command is not a quadratic Bézier, the control point is set to the last path 256 * position. Calling this method adds a [PathNode.ReflectiveQuadTo] to [nodes]. If no contour 257 * has been created by calling [moveTo] first, the origin of the curve is set to (0, 0). 258 * 259 * @param x1 The x coordinate of the end point of the quadratic curve 260 * @param y1 The y coordinate of the end point of the quadratic curve 261 */ reflectiveQuadTonull262 fun reflectiveQuadTo(x1: Float, y1: Float): PathBuilder { 263 _nodes.add(PathNode.ReflectiveQuadTo(x1, y1)) 264 return this 265 } 266 267 /** 268 * Add a quadratic Bézier by adding a [PathNode.RelativeReflectiveQuadTo] to [nodes]. If no 269 * contour has been created by calling [moveTo] first, the origin of the curve is set to (0, 0). 270 * The quadratic Bézier end point is defined by an offset relative to the last point. The 271 * reflective nature of the curve is described in [reflectiveQuadTo]. 272 * 273 * @param dx1 The x offset of the end point of the quadratic curve, relative to the last path 274 * position 275 * @param dy1 The y offset of the end point of the quadratic curve, relative to the last path 276 * position 277 */ reflectiveQuadToRelativenull278 fun reflectiveQuadToRelative(dx1: Float, dy1: Float): PathBuilder { 279 _nodes.add(PathNode.RelativeReflectiveQuadTo(dx1, dy1)) 280 return this 281 } 282 283 /** 284 * Add an elliptical arc from the last point to the position ([x1], [y1]) by adding 285 * [PathNode.ArcTo] to [nodes]. If no contour has been created by calling [moveTo] first, the 286 * origin of the arc is set to (0, 0). 287 * 288 * The ellipse is defined by 3 parameters: 289 * - [horizontalEllipseRadius] and [verticalEllipseRadius] to define the size of the ellipse 290 * - [theta] to define the orientation (as an X-axis rotation) of the ellipse 291 * 292 * In most situations, there are four arc candidates that can be drawn from the origin to ([x1], 293 * [y1]). Which of the arcs is used is influenced by [isMoreThanHalf] and [isPositiveArc]. 294 * 295 * When [isMoreThanHalf] is set to `true`, the added arc will be chosen amongst the two 296 * candidates that represent an arc sweep greater than or equal to 180 degrees. 297 * 298 * When [isPositiveArc] is set to `true`, the added arc will be chosen amongst the two 299 * candidates with a positive-angle direction (counter-clockwise) 300 * 301 * @param horizontalEllipseRadius The horizontal radius of the ellipse 302 * @param verticalEllipseRadius The vertical radius of the ellipse 303 * @param theta The rotation of the ellipse around the X-axis, in degrees 304 * @param isMoreThanHalf Defines whether to use an arc candidate with a sweep greater than or 305 * equal to 180 degrees 306 * @param isPositiveArc Defines whether to use an arc candidate that's counter-clockwise or not 307 * @param x1 The x coordinate of the end point of the arc 308 * @param y1 The y coordinate of the end point of the arc 309 */ arcTonull310 fun arcTo( 311 horizontalEllipseRadius: Float, 312 verticalEllipseRadius: Float, 313 theta: Float, 314 isMoreThanHalf: Boolean, 315 isPositiveArc: Boolean, 316 x1: Float, 317 y1: Float 318 ): PathBuilder { 319 _nodes.add( 320 PathNode.ArcTo( 321 horizontalEllipseRadius, 322 verticalEllipseRadius, 323 theta, 324 isMoreThanHalf, 325 isPositiveArc, 326 x1, 327 y1 328 ) 329 ) 330 return this 331 } 332 333 /** 334 * Add an elliptical arc by adding [PathNode.RelativeArcTo] to [nodes]. If no contour has been 335 * created by calling [moveTo] first, the origin of the arc is set to (0, 0). The arc Bézier end 336 * point is defined by an offset relative to the last point. 337 * 338 * The ellipse is defined by 3 parameters: 339 * - [a] and [b] to define the size of the ellipse 340 * - [theta] to define the orientation (as an X-axis rotation) of the ellipse 341 * 342 * In most situations, there are four arc candidates that can be drawn from the origin to the 343 * end point. Which of the arcs is used is influenced by [isMoreThanHalf] and [isPositiveArc]. 344 * 345 * When [isMoreThanHalf] is set to `true`, the added arc will be chosen amongst the two 346 * candidates that represent an arc sweep greater than or equal to 180 degrees. 347 * 348 * When [isPositiveArc] is set to `true`, the added arc will be chosen amongst the two 349 * candidates with a positive-angle direction (counter-clockwise) 350 * 351 * @param a The horizontal radius of the ellipse 352 * @param b The vertical radius of the ellipse 353 * @param theta The rotation of the ellipse around the X-axis, in degrees 354 * @param isMoreThanHalf Defines whether to use an arc candidate with a sweep greater than or 355 * equal to 180 degrees 356 * @param isPositiveArc Defines whether to use an arc candidate that's counter-clockwise or not 357 * @param dx1 The x offset of the end point of the arc, relative to the last path position 358 * @param dy1 The y offset of the end point of the arc, relative to the last path position 359 */ arcToRelativenull360 fun arcToRelative( 361 a: Float, 362 b: Float, 363 theta: Float, 364 isMoreThanHalf: Boolean, 365 isPositiveArc: Boolean, 366 dx1: Float, 367 dy1: Float 368 ): PathBuilder { 369 _nodes.add(PathNode.RelativeArcTo(a, b, theta, isMoreThanHalf, isPositiveArc, dx1, dy1)) 370 return this 371 } 372 } 373