1 /*
2  * Copyright 2024 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 package androidx.xr.runtime.math
18 
19 import kotlin.math.abs
20 import kotlin.math.acos
21 import kotlin.math.max
22 import kotlin.math.min
23 import kotlin.math.sqrt
24 
25 /**
26  * Represents a three-dimensional position in space.
27  *
28  * The coordinate system is right-handed. The [x]-axis points to the right, the [y]-axis up and the
29  * [z]-axis back.
30  *
31  * @property x the value of the horizontal component.
32  * @property y the value of the vertical component.
33  * @property z the value of the forward component.
34  */
35 public class Vector3
36 @JvmOverloads
37 constructor(public val x: Float = 0F, public val y: Float = 0F, public val z: Float = 0F) {
38     /** The squared length of the vector. */
39     public inline val lengthSquared: Float
40         get() = x * x + y * y + z * z
41 
42     /** The length of the vector. */
43     public inline val length: Float
44         get() = sqrt(lengthSquared)
45 
46     /** Creates a new vector with the same values as the [other] vector. */
47     public constructor(other: Vector3) : this(other.x, other.y, other.z)
48 
49     /** Negates this vector. */
unaryMinusnull50     public operator fun unaryMinus(): Vector3 = Vector3(-x, -y, -z)
51 
52     /** Returns a new vector with the sum of this vector and the [other] vector. */
53     public operator fun plus(other: Vector3): Vector3 =
54         Vector3(x + other.x, y + other.y, z + other.z)
55 
56     /** Returns a new vector with the difference of this vector and the [other] vector. */
57     public operator fun minus(other: Vector3): Vector3 =
58         Vector3(x - other.x, y - other.y, z - other.z)
59 
60     /** Get a new vector multiplied by a scalar amount. */
61     public operator fun times(c: Float): Vector3 = Vector3(x * c, y * c, z * c)
62 
63     /** Returns a new vector with the product of this vector and the [other] vector. */
64     public operator fun times(other: Vector3): Vector3 =
65         Vector3(x * other.x, y * other.y, z * other.z)
66 
67     /** Returns a new vector with this vector divided by a scalar amount. */
68     public operator fun div(c: Float): Vector3 = Vector3(x / c, y / c, z / c)
69 
70     /** Returns a new vector with this vector divided by the [other] vector. */
71     public operator fun div(other: Vector3): Vector3 =
72         Vector3(x / other.x, y / other.y, z / other.z)
73 
74     /** Returns the dot product of this vector and the [other] vector. */
75     public infix fun dot(other: Vector3): Float = x * other.x + y * other.y + z * other.z
76 
77     /** Returns the cross product of this vector and the [other] vector. */
78     public infix fun cross(other: Vector3): Vector3 =
79         Vector3(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x)
80 
81     /** Returns the normalized version of this vector. */
82     public fun toNormalized(): Vector3 {
83         val norm = rsqrt(lengthSquared)
84 
85         return Vector3(x * norm, y * norm, z * norm)
86     }
87 
88     /** Returns a new vector with its values clamped between [min] and [max] vectors. */
clampnull89     public fun clamp(min: Vector3, max: Vector3): Vector3 {
90         var clampedX = clamp(x, min.x, max.x)
91         var clampedY = clamp(y, min.y, max.y)
92         var clampedZ = clamp(z, min.z, max.z)
93 
94         return Vector3(clampedX, clampedY, clampedZ)
95     }
96 
97     /** Returns a copy of the vector. */
98     @JvmOverloads
copynull99     public fun copy(x: Float = this.x, y: Float = this.y, z: Float = this.z): Vector3 =
100         Vector3(x, y, z)
101 
102     /** Returns true if this vector is equal to [other]. */
103     override fun equals(other: Any?): Boolean {
104         if (this === other) return true
105         if (other !is Vector3) return false
106 
107         return this.x == other.x && this.y == other.y && this.z == other.z
108     }
109 
hashCodenull110     override fun hashCode(): Int {
111         var result = x.hashCode()
112         result = 31 * result + y.hashCode()
113         result = 31 * result + z.hashCode()
114         return result
115     }
116 
toStringnull117     override fun toString(): String = "[x=$x, y=$y, z=$z]"
118 
119     public companion object {
120         /** Vector with all components set to zero. */
121         @JvmField public val Zero: Vector3 = Vector3(x = 0f, y = 0f, z = 0f)
122 
123         /** Vector with all components set to one. */
124         @JvmField public val One: Vector3 = Vector3(x = 1f, y = 1f, z = 1f)
125 
126         /** Vector with y set to one and all other components set to zero. */
127         @JvmField public val Up: Vector3 = Vector3(x = 0f, y = 1f, z = 0f)
128 
129         /** Vector with y set to negative one and all other components set to zero. */
130         @JvmField public val Down: Vector3 = Vector3(x = 0f, y = -1f, z = 0f)
131 
132         /** Vector with x set to negative one and all other components set to zero. */
133         @JvmField public val Left: Vector3 = Vector3(x = -1f, y = 0f, z = 0f)
134 
135         /** Vector with x set to one and all other components set to zero. */
136         @JvmField public val Right: Vector3 = Vector3(x = 1f, y = 0f, z = 0f)
137 
138         /** Vector with z set to one and all other components set to zero. */
139         @JvmField public val Backward: Vector3 = Vector3(x = 0f, y = 0f, z = 1f)
140 
141         /** Vector with z set to negative one and all other components set to zero. */
142         @JvmField public val Forward: Vector3 = Vector3(x = 0f, y = 0f, z = -1f)
143 
144         /** Creates a new vector with all components set to [value]. */
145         @JvmStatic public fun fromValue(value: Float): Vector3 = Vector3(value, value, value)
146 
147         /**
148          * Returns the angle between this vector and the [other] vector in degrees. The result is
149          * never greater than 180 degrees.
150          */
151         @JvmStatic
152         public fun angleBetween(vector1: Vector3, vector2: Vector3): Float {
153             val dot = vector1 dot vector2
154             val magnitude = vector1.length * vector2.length
155 
156             if (magnitude < 1e-10f) {
157                 return 0.0f
158             }
159 
160             // Clamp due to floating point precision errors that could cause dot to be > mag.
161             // Would cause acos to return NaN.
162             val cos = clamp(dot / magnitude, -1.0f, 1.0f)
163 
164             return acos(cos)
165         }
166 
167         /** Returns the distance between this vector and the [other] vector. */
168         @JvmStatic
169         public fun distance(vector1: Vector3, vector2: Vector3): Float = (vector1 - vector2).length
170 
171         /**
172          * Returns a new vector that is linearly interpolated between [start] and [end] using the
173          * interpolated amount [ratio].
174          *
175          * If [ratio] is outside of the range `[0, 1]`, the returned vector will be extrapolated.
176          */
177         @JvmStatic
178         public fun lerp(start: Vector3, end: Vector3, ratio: Float): Vector3 =
179             Vector3(
180                 lerp(start.x, end.x, ratio),
181                 lerp(start.y, end.y, ratio),
182                 lerp(start.z, end.z, ratio)
183             )
184 
185         /** Returns the minimum of each component of the two vectors. */
186         @JvmStatic
187         public fun min(a: Vector3, b: Vector3): Vector3 =
188             Vector3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z))
189 
190         /** Returns the maximum of each component of the two vectors. */
191         @JvmStatic
192         public fun max(a: Vector3, b: Vector3): Vector3 =
193             Vector3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z))
194 
195         /** Computes the vector projected from [vector] onto [planeNormal]. */
196         @JvmStatic
197         public fun projectOnPlane(vector: Vector3, planeNormal: Vector3): Vector3 =
198             vector - planeNormal * (vector dot planeNormal) / (planeNormal dot planeNormal)
199 
200         /** Returns the absolute values of each component of the vector. */
201         @JvmStatic
202         public fun abs(vector: Vector3): Vector3 =
203             Vector3(abs(vector.x), abs(vector.y), abs(vector.z))
204     }
205 }
206