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 @file:Suppress("NOTHING_TO_INLINE")
17
18 package androidx.compose.ui.util
19
20 import kotlin.math.abs
21 import kotlin.math.floor
22 import kotlin.math.roundToLong
23
24 /** Linearly interpolate between [start] and [stop] with [fraction] fraction between them. */
lerpnull25 fun lerp(start: Float, stop: Float, fraction: Float): Float {
26 return (1 - fraction) * start + fraction * stop
27 }
28
29 /** Linearly interpolate between [start] and [stop] with [fraction] fraction between them. */
lerpnull30 fun lerp(start: Int, stop: Int, fraction: Float): Int {
31 return start + ((stop - start) * fraction.toDouble()).fastRoundToInt()
32 }
33
34 /** Linearly interpolate between [start] and [stop] with [fraction] fraction between them. */
lerpnull35 fun lerp(start: Long, stop: Long, fraction: Float): Long {
36 return start + ((stop - start) * fraction.toDouble()).roundToLong()
37 }
38
39 /**
40 * Returns the smaller of the given values. If any value is NaN, returns NaN. Preferred over
41 * `kotlin.comparisons.minfOf()` for 4 arguments as it avoids allocating an array because of the
42 * varargs.
43 */
fastMinOfnull44 inline fun fastMinOf(a: Float, b: Float, c: Float, d: Float): Float {
45 // ART inlines everything and generates only 3 fmin instructions
46 return minOf(a, minOf(b, minOf(c, d)))
47 }
48
49 /**
50 * Returns the largest of the given values. If any value is NaN, returns NaN. Preferred over
51 * `kotlin.comparisons.maxOf()` for 4 arguments as it avoids allocating an array because of the
52 * varargs.
53 */
fastMaxOfnull54 inline fun fastMaxOf(a: Float, b: Float, c: Float, d: Float): Float {
55 // ART inlines everything and generates only 3 fmax instructions
56 return maxOf(a, maxOf(b, maxOf(c, d)))
57 }
58
59 /**
60 * Returns this float value clamped in the inclusive range defined by [minimumValue] and
61 * [maximumValue]. Unlike [Float.coerceIn], the range is not validated: the caller must ensure that
62 * [minimumValue] is less than [maximumValue].
63 */
fastCoerceInnull64 inline fun Float.fastCoerceIn(minimumValue: Float, maximumValue: Float) =
65 this.fastCoerceAtLeast(minimumValue).fastCoerceAtMost(maximumValue)
66
67 /** Ensures that this value is not less than the specified [minimumValue]. */
68 inline fun Float.fastCoerceAtLeast(minimumValue: Float): Float {
69 return if (this < minimumValue) minimumValue else this
70 }
71
72 /** Ensures that this value is not greater than the specified [maximumValue]. */
fastCoerceAtMostnull73 inline fun Float.fastCoerceAtMost(maximumValue: Float): Float {
74 return if (this > maximumValue) maximumValue else this
75 }
76
77 /**
78 * Returns this double value clamped in the inclusive range defined by [minimumValue] and
79 * [maximumValue]. Unlike [Float.coerceIn], the range is not validated: the caller must ensure that
80 * [minimumValue] is less than [maximumValue].
81 */
fastCoerceInnull82 inline fun Double.fastCoerceIn(minimumValue: Double, maximumValue: Double) =
83 this.fastCoerceAtLeast(minimumValue).fastCoerceAtMost(maximumValue)
84
85 /** Ensures that this value is not less than the specified [minimumValue]. */
86 inline fun Double.fastCoerceAtLeast(minimumValue: Double): Double {
87 return if (this < minimumValue) minimumValue else this
88 }
89
90 /** Ensures that this value is not greater than the specified [maximumValue]. */
fastCoerceAtMostnull91 inline fun Double.fastCoerceAtMost(maximumValue: Double): Double {
92 return if (this > maximumValue) maximumValue else this
93 }
94
95 /**
96 * Returns this integer value clamped in the inclusive range defined by [minimumValue] and
97 * [maximumValue]. Unlike [Int.coerceIn], the range is not validated: the caller must ensure that
98 * [minimumValue] is less than [maximumValue].
99 */
fastCoerceInnull100 inline fun Int.fastCoerceIn(minimumValue: Int, maximumValue: Int) =
101 this.fastCoerceAtLeast(minimumValue).fastCoerceAtMost(maximumValue)
102
103 /** Ensures that this value is not less than the specified [minimumValue]. */
104 inline fun Int.fastCoerceAtLeast(minimumValue: Int): Int {
105 return if (this < minimumValue) minimumValue else this
106 }
107
108 /** Ensures that this value is not greater than the specified [maximumValue]. */
fastCoerceAtMostnull109 inline fun Int.fastCoerceAtMost(maximumValue: Int): Int {
110 return if (this > maximumValue) maximumValue else this
111 }
112
113 /**
114 * Returns this long value clamped in the inclusive range defined by [minimumValue] and
115 * [maximumValue]. Unlike [Long.coerceIn], the range is not validated: the caller must ensure that
116 * [minimumValue] is less than [maximumValue].
117 */
fastCoerceInnull118 inline fun Long.fastCoerceIn(minimumValue: Long, maximumValue: Long) =
119 this.fastCoerceAtLeast(minimumValue).fastCoerceAtMost(maximumValue)
120
121 /** Ensures that this value is not less than the specified [minimumValue]. */
122 inline fun Long.fastCoerceAtLeast(minimumValue: Long): Long {
123 return if (this < minimumValue) minimumValue else this
124 }
125
126 /** Ensures that this value is not greater than the specified [maximumValue]. */
fastCoerceAtMostnull127 inline fun Long.fastCoerceAtMost(maximumValue: Long): Long {
128 return if (this > maximumValue) maximumValue else this
129 }
130
131 /**
132 * Returns `true` if this float is a finite floating-point value; returns `false` otherwise (for
133 * `NaN` and infinity).
134 */
fastIsFinitenull135 inline fun Float.fastIsFinite(): Boolean {
136 // TODO: We can delegate back to Float.isFinite() when
137 // https://youtrack.jetbrains.com/issue/KT-70695 is fixed and Compose depends on the proper
138 // version of Kotlin
139 return (toRawBits() and 0x7fffffff) < 0x7f800000
140 }
141
142 /**
143 * Returns `true` if this double is a finite floating-point value; returns `false` otherwise (for
144 * `NaN` and infinity).
145 */
fastIsFinitenull146 inline fun Double.fastIsFinite(): Boolean {
147 // TODO: We can delegate back to Float.isFinite() when
148 // https://youtrack.jetbrains.com/issue/KT-70695 is fixed and Compose depends on the proper
149 // version of Kotlin
150 return (toRawBits() and 0x7fffffff_ffffffffL) < 0x7ff00000_00000000L
151 }
152
153 /**
154 * Fast, approximate cube root function. Returns the cube root of [x]; for any [x] `fastCbrt(-x) ==
155 * -fastCbrt(x)`.
156 *
157 * When [x] is:
158 * - [Float.NaN], returns [Float.NaN]
159 * - [Float.POSITIVE_INFINITY], returns [Float.NaN]
160 * - [Float.NEGATIVE_INFINITY], returns [Float.NaN]
161 * - Zero, returns a value close to 0 (~8.3e-14) with the same sign
162 *
163 * The maximum error compared to [kotlin.math.cbrt] is:
164 * - 5.9604645E-7 in the range -1f..1f
165 * - 4.7683716E-6 in the range -256f..256f
166 * - 3.8146973E-5 in the range -65_536f..65_536f
167 * - 1.5258789E-4 in the range -16_777_216..16_777_216f
168 */
fastCbrtnull169 fun fastCbrt(x: Float): Float {
170 // Our fast cube root approximation is implemented using the binary
171 // representation of a float as a log space (log2 in our case). In
172 // log space, we can reason about the cube root function in a
173 // different way:
174 //
175 // log2(cbrt(x)) = log2(x^1/3) = 1/3 * log2(x)
176 //
177 // Assuming x is a positive normal number, it can be written as:
178 //
179 // x = 2^e_x * (1 + m_x)
180 //
181 // Therefore:
182 //
183 // log2(x) = e_x + log2(1 + m_x)
184 //
185 // Since the m_x is in the range [0, 1), we can apply the following
186 // approximation:
187 //
188 // log2(1 + m_x) ~= m_x + σ
189 //
190 // All together, we end up with:
191 //
192 // log2(x) = e_x + m_x + σ
193 //
194 // Using the binary/integer representation I_x of a float:
195 //
196 // I_x = E_x * L + M_x
197 //
198 // Where:
199 // - B is the exponent bias, or B = 127 for single precision floats
200 // - E_x is the biased exponent, or E_x = e_x + B
201 // - L is the magnitude of the significand, or L = 2^23
202 // - M_x is the significand M_x = m_x * L
203 //
204 // I_x = E_x * L + M_x
205 // = L * (e_x + B) + L * m_x
206 // = L * (e_x + m_x + B)
207 // = L * (e_x + m_x + σ + B - σ)
208 // ~= L * (log2(x) + B - σ)
209 // ~= L * log2(x) + L * (B - σ)
210 //
211 // We have thus:
212 //
213 // log2(x) ~= I_x / L - (B - σ)
214 //
215 // With:
216 //
217 // y = x^(1/3)
218 //
219 // We have:
220 //
221 // log2(y) = 1/3 * log2(x)
222 //
223 // I_y / L - (B - σ) ~!= 1/3 * (I_x / L - (B - σ))
224 //
225 // By simplification:
226 //
227 // I_y ~= 1/3 * L * (B - σ) + 1/3 * I_x
228 //
229 // We now need to find a good value for 1/3 * L * (B - σ),
230 // which is equivalent to finding a good σ since L and B are fixed.
231 //
232 // Reusing the previous simplification, the approximation for the
233 // cube root of 1 would be:
234 //
235 // I(1^1/3) ~= 1/3 * L * (B - σ) + 1/3 * I(1)
236 //
237 // 1/3 * L * (B - σ) ~= I(1^1/3) - 1/3 * I(1)
238 //
239 // Since I(1^1/3) == I(1):
240 //
241 // 1/3 * L * (B - σ) ~= 2/3 * I(1)
242 //
243 // For single precision floats:
244 //
245 // I(1) = 0x3f800000
246 //
247 // 2/3 * I(1) = 0x2a555555
248 //
249 // All together, we get:
250 //
251 // I_y = 0x2a555555 + I_x / 3
252 //
253 // Finally by going going back from an integer representation to a single
254 // precision float, we obtain our first approximation of the cube root.
255 //
256 // We further improve that approximation by using two rounds of the Newton-
257 // Rhapson method. One round proved not precise enough for our needs, and
258 // more rounds don't improve the results significantly given our use cases.
259 //
260 // Note: the constant 0x2a555555 we computed above is only a standalone
261 // approximation that doesn't account for the subsequent Newton-Rhapson
262 // refinements. The approximation can be improved for Newton-Rhapson by
263 // debiasing it. To debias 0x2a555555, we just use a brute-force method to
264 // minimize the error between cbrt() and this function. Doing so gives us
265 // a new constant, 0x2a510554L, which greatly improves the maximum error:
266 // - 6.2584877E-6 -> 5.9604645E-7 in the range -1f..1f
267 // - 5.0067900E-5 -> 4.7683716E-6 in the range -256f..256f
268 // - 4.0054320E-4 -> 3.8146973E-5 in the range -65_536f..65_536f
269 // - 1.6021729E-3 -> 1.5258789E-4 in the range -16_777_216..16_777_216f
270 val v = x.toRawBits().toLong() and 0x1ffffffffL
271 var estimate = floatFromBits(0x2a510554 + (v / 3).toInt())
272
273 // 2 rounds of the Newton-Rhapson method to improve accuracy
274 estimate -= (estimate - x / (estimate * estimate)) * (1.0f / 3.0f)
275 estimate -= (estimate - x / (estimate * estimate)) * (1.0f / 3.0f)
276
277 return estimate
278 }
279
280 /**
281 * Fast, approximate sine function. Returns the sine of the angle [normalizedDegrees] expressed in
282 * normalized degrees. For instance, to compute the sine of 180 degrees, you should pass `0.5f`
283 * (`180.0f/360.0f`). To compute the sine of any angle in degrees, call the function this way:
284 * ```
285 * val s = normalizedAngleSin(angleInDegrees * (1.0f / 360.0f))
286 * ```
287 *
288 * If you are compute the sine and the cosine of an angle at the same time, you can reuse the
289 * normalized angle:
290 * ```
291 * val normalizedAngle = angleInDegrees * (1.0f / 360.0f)
292 * val s = normalizedAngleSin(normalizedAngle)
293 * val c = normalizedAngleCos(normalizedAngle)
294 * ```
295 *
296 * The maximum error of this function in the range 0..360 degrees (0..1 as passed to the function)
297 * is 1.63197e-3, or ~0.0935 degrees.
298 *
299 * When [normalizedDegrees] is:
300 * - [Float.NaN], returns [Float.NaN]
301 * - [Float.POSITIVE_INFINITY], returns [Float.NaN]
302 * - [Float.NEGATIVE_INFINITY], returns [Float.NaN]
303 * - 0f, 0.25f, 0.5f, 0.75f, or 1.0f (0, 90, 180, 360 degrees), the returned value is exact
304 */
normalizedAngleSinnull305 inline fun normalizedAngleSin(normalizedDegrees: Float): Float {
306 val degrees = normalizedDegrees - floor(normalizedDegrees + 0.5f)
307 val x = 2.0f * abs(degrees)
308 val a = 1.0f - x
309 return 8.0f * degrees * a / (1.25f - x * a)
310 }
311
312 /**
313 * Fast, approximate sine function. Returns the sine of the angle [normalizedDegrees] expressed in
314 * normalized degrees. For instance, to compute the sine of 180 degrees, you should pass `0.5f`
315 * (`180.0f/360.0f`). To compute the cosine of any angle in degrees, call the function this way:
316 * ```
317 * val c = normalizedAngleCos(angleInDegrees * (1.0f / 360.0f))
318 * ```
319 *
320 * If you are compute the sine and the cosine of an angle at the same time, you can reuse the
321 * normalized angle:
322 * ```
323 * val normalizedAngle = angleInDegrees * (1.0f / 360.0f)
324 * val s = normalizedAngleSin(normalizedAngle)
325 * val c = normalizedAngleCos(normalizedAngle)
326 * ```
327 *
328 * The maximum error of this function in the range 0..360 degrees (0..1 as passed to the function)
329 * is 1.63231e-3, or ~0.0935 degrees.
330 *
331 * When [normalizedDegrees] is:
332 * - [Float.NaN], returns [Float.NaN]
333 * - [Float.POSITIVE_INFINITY], returns [Float.NaN]
334 * - [Float.NEGATIVE_INFINITY], returns [Float.NaN]
335 * - 0f, 0.25f, 0.5f, 0.75f, or 1.0f (0, 90, 180, 360 degrees), the returned value is exact
336 */
normalizedAngleCosnull337 inline fun normalizedAngleCos(normalizedDegrees: Float): Float =
338 normalizedAngleSin(normalizedDegrees + 0.25f)
339