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