1 /* 2 * Copyright 2023 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 18 19 /** 20 * A path segment represents a curve (line, cubic, quadratic or conic) or a command inside a fully 21 * formed [Path] object. 22 * 23 * A segment is identified by a [type][PathSegment.Type] which in turns defines how many [points] 24 * are available (from 0 to 4 points, each point is represented by 2 floats) and whether the 25 * [weight] is meaningful. Please refer to the documentation of each [type][PathSegment.Type] for 26 * more information. 27 * 28 * A segment with the [Move][Type.Move] or [Close][Type.Close] is usually represented by the 29 * singletons [DoneSegment] and [CloseSegment] respectively. 30 * 31 * @property type The type that identifies this segment and defines the number of points. 32 * @property points An array of points (2 floats per point) describing this segment, whose size 33 * depends on [type]. 34 * @property weight Conic weight, only valid if [type] is [Type.Conic]. See [Type.Conic] for more 35 * information. 36 */ 37 class PathSegment 38 internal constructor( 39 val type: Type, 40 @get:Suppress("ArrayReturn") val points: FloatArray, 41 val weight: Float 42 ) { 43 44 /** 45 * Type of a given segment in a [Path], either a command ([Type.Move], [Type.Close], 46 * [Type.Done]) or a curve ([Type.Line], [Type.Cubic], [Type.Quadratic], [Type.Conic]). 47 */ 48 enum class Type { 49 /** 50 * Move command, the path segment contains 1 point indicating the move destination. The 51 * weight is set 0.0f and not meaningful. 52 */ 53 Move, 54 /** 55 * Line curve, the path segment contains 2 points indicating the two extremities of the 56 * line. The weight is set 0.0f and not meaningful. 57 */ 58 Line, 59 /** 60 * Quadratic curve, the path segment contains 3 points in the following order: 61 * - Start point 62 * - Control point 63 * - End point 64 * 65 * The weight is set 0.0f and not meaningful. 66 */ 67 Quadratic, 68 /** 69 * Conic curve, the path segment contains 3 points in the following order: 70 * - Start point 71 * - Control point 72 * - End point 73 * 74 * The curve is weighted by the [weight][PathSegment.weight] property. 75 * 76 * The conic weight determines the amount of influence conic control point has on the curve. 77 * If the weight is less than one, the curve represents an elliptical section. If the weight 78 * is greater than one, the curve represents a hyperbolic section. If the weight equals one, 79 * the curve represents a parabolic section. 80 */ 81 Conic, 82 /** 83 * Cubic curve, the path segment contains 4 points in the following order: 84 * - Start point 85 * - First control point 86 * - Second control point 87 * - End point 88 * 89 * The weight is set 0.0f and not meaningful. 90 */ 91 Cubic, 92 /** 93 * Close command, close the current contour by joining the last point added to the path with 94 * the first point of the current contour. The segment does not contain any point. The 95 * weight is set 0.0f and not meaningful. 96 */ 97 Close, 98 /** 99 * Done command, which indicates that no further segment will be found in the path. It 100 * typically indicates the end of an iteration over a path and can be ignored. 101 */ 102 Done 103 } 104 equalsnull105 override fun equals(other: Any?): Boolean { 106 if (this === other) return true 107 if (other == null || this::class != other::class) return false 108 109 other as PathSegment 110 111 if (type != other.type) return false 112 if (!points.contentEquals(other.points)) return false 113 if (weight != other.weight) return false 114 115 return true 116 } 117 hashCodenull118 override fun hashCode(): Int { 119 var result = type.hashCode() 120 result = 31 * result + points.contentHashCode() 121 result = 31 * result + weight.hashCode() 122 return result 123 } 124 toStringnull125 override fun toString(): String { 126 return "PathSegment(type=$type, points=${points.contentToString()}, weight=$weight)" 127 } 128 } 129 130 /** 131 * A [PathSegment] containing the [Done][PathSegment.Type.Done] command. This static object exists 132 * to avoid allocating a new segment when returning a [Done][PathSegment.Type.Done] result from 133 * [PathIterator.next]. 134 */ 135 val DoneSegment = PathSegment(PathSegment.Type.Done, FloatArray(0), 0.0f) 136 137 /** 138 * A [PathSegment] containing the [Close][PathSegment.Type.Close] command. This static object exists 139 * to avoid allocating a new segment when returning a [Close][PathSegment.Type.Close] result from 140 * [PathIterator.next]. 141 */ 142 val CloseSegment = PathSegment(PathSegment.Type.Close, FloatArray(0), 0.0f) 143