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