1 /*
2 * Copyright 2020 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:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
18
19 package androidx.compose.ui.unit
20
21 import androidx.compose.runtime.Immutable
22 import androidx.compose.runtime.Stable
23 import androidx.compose.ui.geometry.Offset
24 import androidx.compose.ui.util.fastRoundToInt
25 import androidx.compose.ui.util.lerp
26 import androidx.compose.ui.util.packInts
27 import androidx.compose.ui.util.unpackInt1
28 import androidx.compose.ui.util.unpackInt2
29 import kotlin.jvm.JvmInline
30
31 /** Constructs a [IntOffset] from [x] and [y] position [Int] values. */
IntOffsetnull32 @Stable inline fun IntOffset(x: Int, y: Int): IntOffset = IntOffset(packInts(x, y))
33
34 /**
35 * A two-dimensional position using [Int] pixels for units.
36 *
37 * To create an [IntOffset], call the top-level function that accepts an x/y pair of coordinates:
38 * ```
39 * val offset = IntOffset(x, y)
40 * ```
41 *
42 * The primary constructor of [IntOffset] is intended to be used with the [packedValue] property to
43 * allow storing offsets in arrays or collections of primitives without boxing.
44 *
45 * @param packedValue [Long] value encoding the [x] and [y] components of the [IntOffset]. Encoded
46 * values can be obtained by using the [packedValue] property of existing [IntOffset] instances.
47 */
48 @Immutable
49 @JvmInline
50 value class IntOffset(val packedValue: Long) {
51 /** The horizontal aspect of the position in [Int] pixels. */
52 @Stable
53 val x: Int
54 get() = unpackInt1(packedValue)
55
56 /** The vertical aspect of the position in [Int] pixels. */
57 @Stable
58 val y: Int
59 get() = unpackInt2(packedValue)
60
61 @Stable inline operator fun component1(): Int = x
62
63 @Stable inline operator fun component2(): Int = y
64
65 /** Returns a copy of this IntOffset instance optionally overriding the x or y parameter */
66 fun copy(x: Int = unpackInt1(packedValue), y: Int = unpackInt2(packedValue)) =
67 IntOffset(packInts(x, y))
68
69 /** Subtract a [IntOffset] from another one. */
70 @Stable
71 operator fun minus(other: IntOffset) =
72 IntOffset(
73 packInts(
74 unpackInt1(packedValue) - unpackInt1(other.packedValue),
75 unpackInt2(packedValue) - unpackInt2(other.packedValue)
76 )
77 )
78
79 /** Add a [IntOffset] to another one. */
80 @Stable
81 operator fun plus(other: IntOffset) =
82 IntOffset(
83 packInts(
84 unpackInt1(packedValue) + unpackInt1(other.packedValue),
85 unpackInt2(packedValue) + unpackInt2(other.packedValue)
86 )
87 )
88
89 /** Returns a new [IntOffset] representing the negation of this point. */
90 @Stable
91 operator fun unaryMinus() =
92 IntOffset(packInts(-unpackInt1(packedValue), -unpackInt2(packedValue)))
93
94 /**
95 * Multiplication operator.
96 *
97 * Returns an IntOffset whose coordinates are the coordinates of the left-hand-side operand (an
98 * IntOffset) multiplied by the scalar right-hand-side operand (a Float). The result is rounded
99 * to the nearest integer.
100 */
101 @Stable
102 operator fun times(operand: Float): IntOffset =
103 IntOffset(
104 packInts(
105 (unpackInt1(packedValue) * operand).fastRoundToInt(),
106 (unpackInt2(packedValue) * operand).fastRoundToInt()
107 )
108 )
109
110 /**
111 * Division operator.
112 *
113 * Returns an IntOffset whose coordinates are the coordinates of the left-hand-side operand (an
114 * IntOffset) divided by the scalar right-hand-side operand (a Float). The result is rounded to
115 * the nearest integer.
116 */
117 @Stable
118 operator fun div(operand: Float): IntOffset =
119 IntOffset(
120 packInts(
121 (unpackInt1(packedValue) / operand).fastRoundToInt(),
122 (unpackInt2(packedValue) / operand).fastRoundToInt()
123 )
124 )
125
126 /**
127 * Modulo (remainder) operator.
128 *
129 * Returns an IntOffset whose coordinates are the remainder of dividing the coordinates of the
130 * left-hand-side operand (an IntOffset) by the scalar right-hand-side operand (an Int).
131 */
132 @Stable
133 operator fun rem(operand: Int) =
134 IntOffset(packInts(unpackInt1(packedValue) % operand, unpackInt2(packedValue) % operand))
135
136 @Stable override fun toString(): String = "($x, $y)"
137
138 companion object {
139 val Zero = IntOffset(0x0L)
140 val Max = IntOffset(0x7FFF_FFFF_7FFF_FFFF)
141 }
142 }
143
144 /**
145 * Linearly interpolate between two [IntOffset]s.
146 *
147 * The [fraction] argument represents position on the timeline, with 0.0 meaning that the
148 * interpolation has not started, returning [start] (or something equivalent to [start]), 1.0
149 * meaning that the interpolation has finished, returning [stop] (or something equivalent to
150 * [stop]), and values in between meaning that the interpolation is at the relevant point on the
151 * timeline between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and 1.0, so
152 * negative values and values greater than 1.0 are valid.
153 */
154 @Stable
lerpnull155 fun lerp(start: IntOffset, stop: IntOffset, fraction: Float): IntOffset =
156 IntOffset(packInts(lerp(start.x, stop.x, fraction), lerp(start.y, stop.y, fraction)))
157
158 /** Converts the [IntOffset] to an [Offset]. */
159 @Stable inline fun IntOffset.toOffset() = Offset(x.toFloat(), y.toFloat())
160
161 @Stable operator fun Offset.plus(offset: IntOffset): Offset = Offset(x + offset.x, y + offset.y)
162
163 @Stable operator fun Offset.minus(offset: IntOffset): Offset = Offset(x - offset.x, y - offset.y)
164
165 @Stable operator fun IntOffset.plus(offset: Offset): Offset = Offset(x + offset.x, y + offset.y)
166
167 @Stable operator fun IntOffset.minus(offset: Offset): Offset = Offset(x - offset.x, y - offset.y)
168
169 /** Round a [Offset] down to the nearest [Int] coordinates. */
170 @Stable fun Offset.round(): IntOffset = IntOffset(packInts(x.fastRoundToInt(), y.fastRoundToInt()))
171