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