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