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