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.graphics
20 
21 import androidx.compose.ui.geometry.MutableRect
22 import androidx.compose.ui.geometry.Offset
23 import androidx.compose.ui.geometry.Rect
24 import androidx.compose.ui.util.fastIsFinite
25 import androidx.compose.ui.util.fastMaxOf
26 import androidx.compose.ui.util.fastMinOf
27 import kotlin.math.PI
28 import kotlin.math.cos
29 import kotlin.math.sin
30 
31 // NOTE: This class contains a number of tests like this:
32 //
33 //     `if (values.size < 16) return`
34 //
35 // These tests exist to give the AOT compiler a hint about the size of the array.
36 // Their presence eliminates a large number of array bound checks. For instance,
37 // in the isIdentity method, this eliminates half of the instructions and half
38 // of the branches.
39 //
40 // These tests are not there to generate early returns (the test will always
41 // fail), but only to influence code generation.
42 //
43 // DO NOT REMOVE THOSE TESTS.
44 @kotlin.jvm.JvmInline
45 value class Matrix(
46     val values: FloatArray =
47         floatArrayOf(1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f)
48 ) {
getnull49     inline operator fun get(row: Int, column: Int) = values[(row * 4) + column]
50 
51     inline operator fun set(row: Int, column: Int, v: Float) {
52         values[(row * 4) + column] = v
53     }
54 
55     /** Does the 3D transform on [point] and returns the `x` and `y` values in an [Offset]. */
mapnull56     fun map(point: Offset): Offset {
57         // See top-level comment
58         if (values.size < 16) return point
59 
60         val v00 = this[0, 0]
61         val v01 = this[0, 1]
62         val v03 = this[0, 3]
63         val v10 = this[1, 0]
64         val v11 = this[1, 1]
65         val v13 = this[1, 3]
66         val v30 = this[3, 0]
67         val v31 = this[3, 1]
68         val v33 = this[3, 3]
69 
70         val x = point.x
71         val y = point.y
72         val z = v03 * x + v13 * y + v33
73         val inverseZ = 1 / z
74         val pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
75 
76         return Offset(x = pZ * (v00 * x + v10 * y + v30), y = pZ * (v01 * x + v11 * y + v31))
77     }
78 
79     /** Does a 3D transform on [rect] and returns its bounds after the transform. */
mapnull80     fun map(rect: Rect): Rect {
81         // See top-level comment
82         if (values.size < 16) return rect
83 
84         val v00 = this[0, 0]
85         val v01 = this[0, 1]
86         val v03 = this[0, 3]
87         val v10 = this[1, 0]
88         val v11 = this[1, 1]
89         val v13 = this[1, 3]
90         val v30 = this[3, 0]
91         val v31 = this[3, 1]
92         val v33 = this[3, 3]
93 
94         val l = rect.left
95         val t = rect.top
96         val r = rect.right
97         val b = rect.bottom
98 
99         var x = l
100         var y = t
101         var inverseZ = 1.0f / (v03 * x + v13 * y + v33)
102         var pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
103         val x0 = pZ * (v00 * x + v10 * y + v30)
104         val y0 = pZ * (v01 * x + v11 * y + v31)
105 
106         x = l
107         y = b
108         inverseZ = 1.0f / (v03 * x + v13 * y + v33)
109         pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
110         val x1 = pZ * (v00 * x + v10 * y + v30)
111         val y1 = pZ * (v01 * x + v11 * y + v31)
112 
113         x = r
114         y = t
115         inverseZ = 1.0f / (v03 * x + v13 * y + v33)
116         pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
117         val x2 = pZ * (v00 * x + v10 * y + v30)
118         val y2 = pZ * (v01 * x + v11 * y + v31)
119 
120         x = r
121         y = b
122         inverseZ = 1.0f / (v03 * x + v13 * y + v33)
123         pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
124         val x3 = pZ * (v00 * x + v10 * y + v30)
125         val y3 = pZ * (v01 * x + v11 * y + v31)
126 
127         return Rect(
128             fastMinOf(x0, x1, x2, x3),
129             fastMinOf(y0, y1, y2, y3),
130             fastMaxOf(x0, x1, x2, x3),
131             fastMaxOf(y0, y1, y2, y3)
132         )
133     }
134 
135     /** Does a 3D transform on [rect], transforming [rect] with the results. */
mapnull136     fun map(rect: MutableRect) {
137         // See top-level comment
138         if (values.size < 16) return
139 
140         val v00 = this[0, 0]
141         val v01 = this[0, 1]
142         val v03 = this[0, 3]
143         val v10 = this[1, 0]
144         val v11 = this[1, 1]
145         val v13 = this[1, 3]
146         val v30 = this[3, 0]
147         val v31 = this[3, 1]
148         val v33 = this[3, 3]
149 
150         val l = rect.left
151         val t = rect.top
152         val r = rect.right
153         val b = rect.bottom
154 
155         var x = l
156         var y = t
157         var inverseZ = 1.0f / (v03 * x + v13 * y + v33)
158         var pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
159         val x0 = pZ * (v00 * x + v10 * y + v30)
160         val y0 = pZ * (v01 * x + v11 * y + v31)
161 
162         x = l
163         y = b
164         inverseZ = 1.0f / (v03 * x + v13 * y + v33)
165         pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
166         val x1 = pZ * (v00 * x + v10 * y + v30)
167         val y1 = pZ * (v01 * x + v11 * y + v31)
168 
169         x = r
170         y = t
171         inverseZ = 1.0f / (v03 * x + v13 * y + v33)
172         pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
173         val x2 = pZ * (v00 * x + v10 * y + v30)
174         val y2 = pZ * (v01 * x + v11 * y + v31)
175 
176         x = r
177         y = b
178         inverseZ = 1.0f / (v03 * x + v13 * y + v33)
179         pZ = if (inverseZ.fastIsFinite()) inverseZ else 0f
180         val x3 = pZ * (v00 * x + v10 * y + v30)
181         val y3 = pZ * (v01 * x + v11 * y + v31)
182 
183         rect.left = fastMinOf(x0, x1, x2, x3)
184         rect.top = fastMinOf(y0, y1, y2, y3)
185         rect.right = fastMaxOf(x0, x1, x2, x3)
186         rect.bottom = fastMaxOf(y0, y1, y2, y3)
187     }
188 
189     /** Multiply this matrix by [m] and assign the result to this matrix. */
timesAssignnull190     operator fun timesAssign(m: Matrix) {
191         // See top-level comment
192         val v = values
193         if (v.size < 16) return
194         if (m.values.size < 16) return
195 
196         val v00 = dot(this, 0, m, 0)
197         val v01 = dot(this, 0, m, 1)
198         val v02 = dot(this, 0, m, 2)
199         val v03 = dot(this, 0, m, 3)
200         val v10 = dot(this, 1, m, 0)
201         val v11 = dot(this, 1, m, 1)
202         val v12 = dot(this, 1, m, 2)
203         val v13 = dot(this, 1, m, 3)
204         val v20 = dot(this, 2, m, 0)
205         val v21 = dot(this, 2, m, 1)
206         val v22 = dot(this, 2, m, 2)
207         val v23 = dot(this, 2, m, 3)
208         val v30 = dot(this, 3, m, 0)
209         val v31 = dot(this, 3, m, 1)
210         val v32 = dot(this, 3, m, 2)
211         val v33 = dot(this, 3, m, 3)
212 
213         v[0] = v00
214         v[1] = v01
215         v[2] = v02
216         v[3] = v03
217         v[4] = v10
218         v[5] = v11
219         v[6] = v12
220         v[7] = v13
221         v[8] = v20
222         v[9] = v21
223         v[10] = v22
224         v[11] = v23
225         v[12] = v30
226         v[13] = v31
227         v[14] = v32
228         v[15] = v33
229     }
230 
toStringnull231     override fun toString(): String {
232         return """
233             |${this[0, 0]} ${this[0, 1]} ${this[0, 2]} ${this[0, 3]}|
234             |${this[1, 0]} ${this[1, 1]} ${this[1, 2]} ${this[1, 3]}|
235             |${this[2, 0]} ${this[2, 1]} ${this[2, 2]} ${this[2, 3]}|
236             |${this[3, 0]} ${this[3, 1]} ${this[3, 2]} ${this[3, 3]}|
237         """
238             .trimIndent()
239     }
240 
241     /** Invert `this` Matrix. */
invertnull242     fun invert() {
243         // See top-level comment
244         if (values.size < 16) return
245 
246         val a00 = this[0, 0]
247         val a01 = this[0, 1]
248         val a02 = this[0, 2]
249         val a03 = this[0, 3]
250         val a10 = this[1, 0]
251         val a11 = this[1, 1]
252         val a12 = this[1, 2]
253         val a13 = this[1, 3]
254         val a20 = this[2, 0]
255         val a21 = this[2, 1]
256         val a22 = this[2, 2]
257         val a23 = this[2, 3]
258         val a30 = this[3, 0]
259         val a31 = this[3, 1]
260         val a32 = this[3, 2]
261         val a33 = this[3, 3]
262 
263         val b00 = a00 * a11 - a01 * a10
264         val b01 = a00 * a12 - a02 * a10
265         val b02 = a00 * a13 - a03 * a10
266         val b03 = a01 * a12 - a02 * a11
267         val b04 = a01 * a13 - a03 * a11
268         val b05 = a02 * a13 - a03 * a12
269         val b06 = a20 * a31 - a21 * a30
270         val b07 = a20 * a32 - a22 * a30
271         val b08 = a20 * a33 - a23 * a30
272         val b09 = a21 * a32 - a22 * a31
273         val b10 = a21 * a33 - a23 * a31
274         val b11 = a22 * a33 - a23 * a32
275 
276         val det = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06)
277         if (det == 0.0f) {
278             return
279         }
280 
281         val invDet = 1.0f / det
282         this[0, 0] = ((a11 * b11 - a12 * b10 + a13 * b09) * invDet)
283         this[0, 1] = ((-a01 * b11 + a02 * b10 - a03 * b09) * invDet)
284         this[0, 2] = ((a31 * b05 - a32 * b04 + a33 * b03) * invDet)
285         this[0, 3] = ((-a21 * b05 + a22 * b04 - a23 * b03) * invDet)
286         this[1, 0] = ((-a10 * b11 + a12 * b08 - a13 * b07) * invDet)
287         this[1, 1] = ((a00 * b11 - a02 * b08 + a03 * b07) * invDet)
288         this[1, 2] = ((-a30 * b05 + a32 * b02 - a33 * b01) * invDet)
289         this[1, 3] = ((a20 * b05 - a22 * b02 + a23 * b01) * invDet)
290         this[2, 0] = ((a10 * b10 - a11 * b08 + a13 * b06) * invDet)
291         this[2, 1] = ((-a00 * b10 + a01 * b08 - a03 * b06) * invDet)
292         this[2, 2] = ((a30 * b04 - a31 * b02 + a33 * b00) * invDet)
293         this[2, 3] = ((-a20 * b04 + a21 * b02 - a23 * b00) * invDet)
294         this[3, 0] = ((-a10 * b09 + a11 * b07 - a12 * b06) * invDet)
295         this[3, 1] = ((a00 * b09 - a01 * b07 + a02 * b06) * invDet)
296         this[3, 2] = ((-a30 * b03 + a31 * b01 - a32 * b00) * invDet)
297         this[3, 3] = ((a20 * b03 - a21 * b01 + a22 * b00) * invDet)
298     }
299 
300     /** Resets the `this` to the identity matrix. */
resetnull301     fun reset() {
302         // See top-level comment
303         val v = values
304         if (v.size < 16) return
305         v[0] = 1f
306         v[1] = 0f
307         v[2] = 0f
308         v[3] = 0f
309         v[4] = 0f
310         v[5] = 1f
311         v[6] = 0f
312         v[7] = 0f
313         v[8] = 0f
314         v[9] = 0f
315         v[10] = 1f
316         v[11] = 0f
317         v[12] = 0f
318         v[13] = 0f
319         v[14] = 0f
320         v[15] = 1f
321     }
322 
323     /** Sets the entire matrix to the matrix in [matrix]. */
setFromnull324     fun setFrom(matrix: Matrix) {
325         val src = values
326         val dst = matrix.values
327 
328         // See top-level comment
329         if (src.size < 16) return
330         if (dst.size < 16) return
331 
332         src[0] = dst[0]
333         src[1] = dst[1]
334         src[2] = dst[2]
335         src[3] = dst[3]
336         src[4] = dst[4]
337         src[5] = dst[5]
338         src[6] = dst[6]
339         src[7] = dst[7]
340         src[8] = dst[8]
341         src[9] = dst[9]
342         src[10] = dst[10]
343         src[11] = dst[11]
344         src[12] = dst[12]
345         src[13] = dst[13]
346         src[14] = dst[14]
347         src[15] = dst[15]
348     }
349 
350     /** Applies a [degrees] rotation around X to `this`. */
rotateXnull351     fun rotateX(degrees: Float) {
352         // See top-level comment
353         if (values.size < 16) return
354 
355         val r = degrees * (PI / 180.0)
356         val s = sin(r).toFloat()
357         val c = cos(r).toFloat()
358 
359         val a01 = this[0, 1]
360         val a02 = this[0, 2]
361         val v01 = a01 * c - a02 * s
362         val v02 = a01 * s + a02 * c
363 
364         val a11 = this[1, 1]
365         val a12 = this[1, 2]
366         val v11 = a11 * c - a12 * s
367         val v12 = a11 * s + a12 * c
368 
369         val a21 = this[2, 1]
370         val a22 = this[2, 2]
371         val v21 = a21 * c - a22 * s
372         val v22 = a21 * s + a22 * c
373 
374         val a31 = this[3, 1]
375         val a32 = this[3, 2]
376         val v31 = a31 * c - a32 * s
377         val v32 = a31 * s + a32 * c
378 
379         this[0, 1] = v01
380         this[0, 2] = v02
381         this[1, 1] = v11
382         this[1, 2] = v12
383         this[2, 1] = v21
384         this[2, 2] = v22
385         this[3, 1] = v31
386         this[3, 2] = v32
387     }
388 
389     /** Applies a [degrees] rotation around Y to `this`. */
rotateYnull390     fun rotateY(degrees: Float) {
391         // See top-level comment
392         if (values.size < 16) return
393 
394         val r = degrees * (PI / 180.0)
395         val s = sin(r).toFloat()
396         val c = cos(r).toFloat()
397 
398         val a00 = this[0, 0]
399         val a02 = this[0, 2]
400         val v00 = a00 * c + a02 * s
401         val v02 = -a00 * s + a02 * c
402 
403         val a10 = this[1, 0]
404         val a12 = this[1, 2]
405         val v10 = a10 * c + a12 * s
406         val v12 = -a10 * s + a12 * c
407 
408         val a20 = this[2, 0]
409         val a22 = this[2, 2]
410         val v20 = a20 * c + a22 * s
411         val v22 = -a20 * s + a22 * c
412 
413         val a30 = this[3, 0]
414         val a32 = this[3, 2]
415         val v30 = a30 * c + a32 * s
416         val v32 = -a30 * s + a32 * c
417 
418         this[0, 0] = v00
419         this[0, 2] = v02
420         this[1, 0] = v10
421         this[1, 2] = v12
422         this[2, 0] = v20
423         this[2, 2] = v22
424         this[3, 0] = v30
425         this[3, 2] = v32
426     }
427 
428     /** Applies a [degrees] rotation around Z to `this`. */
rotateZnull429     fun rotateZ(degrees: Float) {
430         // See top-level comment
431         if (values.size < 16) return
432 
433         val r = degrees * (PI / 180.0)
434         val s = sin(r).toFloat()
435         val c = cos(r).toFloat()
436 
437         val a00 = this[0, 0]
438         val a10 = this[1, 0]
439         val v00 = c * a00 + s * a10
440         val v10 = -s * a00 + c * a10
441 
442         val a01 = this[0, 1]
443         val a11 = this[1, 1]
444         val v01 = c * a01 + s * a11
445         val v11 = -s * a01 + c * a11
446 
447         val a02 = this[0, 2]
448         val a12 = this[1, 2]
449         val v02 = c * a02 + s * a12
450         val v12 = -s * a02 + c * a12
451 
452         val a03 = this[0, 3]
453         val a13 = this[1, 3]
454         val v03 = c * a03 + s * a13
455         val v13 = -s * a03 + c * a13
456 
457         this[0, 0] = v00
458         this[0, 1] = v01
459         this[0, 2] = v02
460         this[0, 3] = v03
461         this[1, 0] = v10
462         this[1, 1] = v11
463         this[1, 2] = v12
464         this[1, 3] = v13
465     }
466 
467     /** Scale this matrix by [x], [y], [z] */
scalenull468     fun scale(x: Float = 1f, y: Float = 1f, z: Float = 1f) {
469         // See top-level comment
470         if (values.size < 16) return
471         this[0, 0] *= x
472         this[0, 1] *= x
473         this[0, 2] *= x
474         this[0, 3] *= x
475         this[1, 0] *= y
476         this[1, 1] *= y
477         this[1, 2] *= y
478         this[1, 3] *= y
479         this[2, 0] *= z
480         this[2, 1] *= z
481         this[2, 2] *= z
482         this[2, 3] *= z
483     }
484 
485     /** Translate this matrix by [x], [y], [z] */
translatenull486     fun translate(x: Float = 0f, y: Float = 0f, z: Float = 0f) {
487         // See top-level comment
488         if (values.size < 16) return
489         val t1 = this[0, 0] * x + this[1, 0] * y + this[2, 0] * z + this[3, 0]
490         val t2 = this[0, 1] * x + this[1, 1] * y + this[2, 1] * z + this[3, 1]
491         val t3 = this[0, 2] * x + this[1, 2] * y + this[2, 2] * z + this[3, 2]
492         val t4 = this[0, 3] * x + this[1, 3] * y + this[2, 3] * z + this[3, 3]
493         this[3, 0] = t1
494         this[3, 1] = t2
495         this[3, 2] = t3
496         this[3, 3] = t4
497     }
498 
499     /**
500      * Resets this matrix to a "TRS" (translation, rotation, scale) transform around a pivot point.
501      * The transform operations encoded in the matrix are the following, in this specific order:
502      * - A translation by -[pivotX], -[pivotY]
503      * - A translation by [translationX], [translationY], and [translationZ]
504      * - An X rotation by [rotationX]
505      * - A Y rotation by [rotationY]
506      * - A Z rotation by [rotationZ]
507      * - A scale by [scaleX] and [scaleY]
508      * - A translation by [pivotX], [pivotY]
509      *
510      * Calling this method is equivalent to the following code:
511      * ```
512      * val m: Matrix ...
513      * m.reset()
514      * m.translate(-pivotX, -pivotY)
515      * m *= Matrix().apply {
516      *     translate(translationX, translationY)
517      *     rotateX(rotationX)
518      *     rotateY(rotationY)
519      *     rotateZ(rotationZ)
520      *     scale(scaleX, scaleY)
521      * }
522      * m *= Matrix().apply { translate(pivotX, pivotY) }
523      * ```
524      */
resetToPivotedTransformnull525     fun resetToPivotedTransform(
526         pivotX: Float = 0f,
527         pivotY: Float = 0f,
528         translationX: Float = 0f,
529         translationY: Float = 0f,
530         translationZ: Float = 0f,
531         rotationX: Float = 0f,
532         rotationY: Float = 0f,
533         rotationZ: Float = 0f,
534         scaleX: Float = 1f,
535         scaleY: Float = 1f,
536         scaleZ: Float = 1f
537     ) {
538         // X
539         val rx = rotationX * (PI / 180.0)
540         val rsx = sin(rx).toFloat()
541         val rcx = cos(rx).toFloat()
542 
543         var v11 = rcx
544         var v12 = rsx
545 
546         var v21 = -rsx
547         var v22 = rcx
548 
549         val v31 = translationY * rcx - translationZ * rsx
550         var v32 = translationY * rsx + translationZ * rcx
551 
552         // Y
553         val ry = rotationY * (PI / 180.0)
554         val rsy = sin(ry).toFloat()
555         val rcy = cos(ry).toFloat()
556 
557         var v00 = rcy
558         var v02 = -rsy
559 
560         var v10 = v12 * rsy
561         v12 *= rcy
562 
563         var v20 = v22 * rsy
564         v22 *= rcy
565 
566         val v30 = translationX * rcy + v32 * rsy
567         v32 = -translationX * rsy + v32 * rcy
568 
569         // Z
570         val rz = rotationZ * (PI / 180.0)
571         val rsz = sin(rz).toFloat()
572         val rcz = cos(rz).toFloat()
573 
574         val a10 = v10
575         v10 = -rsz * v00 + rcz * v10
576         v00 = rcz * v00 + rsz * a10
577 
578         var v01 = rsz * v11
579         v11 *= rcz
580 
581         val a12 = v12
582         v12 = -rsz * v02 + rcz * a12
583         v02 = rcz * v02 + rsz * a12
584 
585         v00 *= scaleX
586         v01 *= scaleX
587         v02 *= scaleX
588         v10 *= scaleY
589         v11 *= scaleY
590         v12 *= scaleY
591         v20 *= scaleZ
592         v21 *= scaleZ
593         v22 *= scaleZ
594 
595         // See top-level comment
596         if (values.size < 16) return
597 
598         this[0, 0] = v00
599         this[0, 1] = v01
600         this[0, 2] = v02
601         this[0, 3] = 0f
602         this[1, 0] = v10
603         this[1, 1] = v11
604         this[1, 2] = v12
605         this[1, 3] = 0f
606         this[2, 0] = v20
607         this[2, 1] = v21
608         this[2, 2] = v22
609         this[2, 3] = 0f
610         this[3, 0] = -pivotX * v00 - pivotY * v10 + v30 + pivotX
611         this[3, 1] = -pivotX * v01 - pivotY * v11 + v31 + pivotY
612         this[3, 2] = -pivotX * v02 - pivotY * v12 + v32
613         this[3, 3] = 1f
614     }
615 
616     companion object {
617         /** Index of the flattened array that represents the scale factor along the X axis */
618         const val ScaleX = 0
619 
620         /** Index of the flattened array that represents the skew factor along the Y axis */
621         const val SkewY = 1
622 
623         /** Index of the flattened array that represents the perspective factor along the X axis */
624         const val Perspective0 = 3
625 
626         /** Index of the flattened array that represents the skew factor along the X axis */
627         const val SkewX = 4
628 
629         /** Index of the flattened array that represents the scale factor along the Y axis */
630         const val ScaleY = 5
631 
632         /** Index of the flattened array that represents the perspective factor along the Y axis */
633         const val Perspective1 = 7
634 
635         /** Index of the flattened array that represents the scale factor along the Z axis */
636         const val ScaleZ = 10
637 
638         /** Index of the flattened array that represents the translation along the X axis */
639         const val TranslateX = 12
640 
641         /** Index of the flattened array that represents the translation along the Y axis */
642         const val TranslateY = 13
643 
644         /** Index of the flattened array that represents the translation along the Z axis */
645         const val TranslateZ = 14
646 
647         /** Index of the flattened array that represents the perspective factor along the Z axis */
648         const val Perspective2 = 15
649     }
650 }
651 
dotnull652 private inline fun dot(m1: Matrix, row: Int, m2: Matrix, column: Int): Float {
653     return m1[row, 0] * m2[0, column] +
654         m1[row, 1] * m2[1, column] +
655         m1[row, 2] * m2[2, column] +
656         m1[row, 3] * m2[3, column]
657 }
658 
659 /** Whether the given matrix is the identity matrix. */
Matrixnull660 fun Matrix.isIdentity(): Boolean {
661     // See top-level comment
662     val v = values
663     if (v.size < 16) return false
664     return v[0] == 1f &&
665         v[1] == 0f &&
666         v[2] == 0f &&
667         v[3] == 0f &&
668         v[4] == 0f &&
669         v[5] == 1f &&
670         v[6] == 0f &&
671         v[7] == 0f &&
672         v[8] == 0f &&
673         v[9] == 0f &&
674         v[10] == 1f &&
675         v[11] == 0f &&
676         v[12] == 0f &&
677         v[13] == 0f &&
678         v[14] == 0f &&
679         v[15] == 1f
680 }
681