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 @file:Suppress("NOTHING_TO_INLINE") 18 19 package androidx.xr.runtime.math 20 21 import kotlin.math.abs 22 import kotlin.math.acos 23 import kotlin.math.max 24 import kotlin.math.min 25 import kotlin.math.sqrt 26 27 /** 28 * Represents a position in the 2D plane. 29 * 30 * @property x X component of the vector. 31 * @property y Y component of the vector. 32 */ 33 public class Vector2 @JvmOverloads constructor(public val x: Float = 0F, public val y: Float = 0F) { 34 /** The squared length of the vector. */ 35 public inline val lengthSquared: Float 36 get() = x * x + y * y 37 38 /** The length of the vector. */ 39 public inline val length: Float 40 get() = sqrt(lengthSquared) 41 42 /** Creates a new vector with the same values as the [other] vector. */ 43 public constructor(other: Vector2) : this(other.x, other.y) 44 45 /** Negates the values of this vector. */ unaryMinusnull46 public inline operator fun unaryMinus(): Vector2 = Vector2(-x, -y) 47 48 /** Returns a new vector with the sum of this vector and the [other] vector. */ 49 public operator fun plus(other: Vector2): Vector2 = Vector2(this.x + other.x, this.y + other.y) 50 51 /** Returns a new vector with the difference of this vector and the [other] vector. */ 52 public inline operator fun minus(other: Vector2): Vector2 = 53 Vector2(this.x - other.x, this.y - other.y) 54 55 /** Returns a new vector multiplied by a scalar amount */ 56 public inline operator fun times(c: Float): Vector2 = Vector2(x * c, y * c) 57 58 /** Returns a new vector with the product of this vector and the [other] vector. */ 59 public inline operator fun times(other: Vector2): Vector2 = 60 Vector2(this.x * other.x, this.y * other.y) 61 62 /** Returns a new vector with this vector divided by a scalar amount. */ 63 public inline operator fun div(c: Float): Vector2 = Vector2(x / c, y / c) 64 65 /** Returns a new vector with this vector divided by the [other] vector. */ 66 public inline operator fun div(other: Vector2): Vector2 = Vector2(x / other.x, y / other.y) 67 68 /** Returns a normalized version of this vector. */ 69 public fun toNormalized(): Vector2 { 70 val norm = rsqrt(lengthSquared) 71 72 return Vector2(x * norm, y * norm) 73 } 74 75 /** Returns the cross product of this vector and the [other] vector. */ crossnull76 public inline infix fun cross(other: Vector2): Float = this.x * other.y - this.y * other.x 77 78 /** Returns the dot product of this vector and the [other] vector. */ 79 public inline infix fun dot(other: Vector2): Float = x * other.x + y * other.y 80 81 /** Returns a new vector with the values clamped between [min] and [max] vectors. */ 82 public fun clamp(min: Vector2, max: Vector2): Vector2 { 83 var clampedX = max(x, min.x) 84 var clampedY = max(y, min.y) 85 86 clampedX = min(clampedX, max.x) 87 clampedY = min(clampedY, max.y) 88 89 return Vector2(clampedX, clampedY) 90 } 91 92 /** Returns a copy of the vector. */ 93 @JvmOverloads copynull94 public inline fun copy(x: Float = this.x, y: Float = this.y): Vector2 = Vector2(x, y) 95 96 /** Returns true if this vector is equal to the [other]. */ 97 override fun equals(other: Any?): Boolean { 98 if (this === other) return true 99 if (other !is Vector2) return false 100 101 return this.x == other.x && this.y == other.y 102 } 103 hashCodenull104 override fun hashCode(): Int = 31 * x.hashCode() + y.hashCode() 105 106 override fun toString(): String = "[x=$x, y=$y]" 107 108 public companion object { 109 /** Vector with all components set to zero. */ 110 @JvmField public val Zero: Vector2 = Vector2(x = 0f, y = 0f) 111 112 /** Vector with all components set to one. */ 113 @JvmField public val One: Vector2 = Vector2(x = 1f, y = 1f) 114 115 /** Vector with y set to one and all other components set to zero. */ 116 @JvmField public val Up: Vector2 = Vector2(x = 0f, y = 1f) 117 118 /** Vector with y set to negative one and all other components set to zero. */ 119 @JvmField public val Down: Vector2 = Vector2(x = 0f, y = -1f) 120 121 /** Vector with x set to negative one and all other components set to zero. */ 122 @JvmField public val Left: Vector2 = Vector2(x = -1f, y = 0f) 123 124 /** Vector with x set to one and all other components set to zero. */ 125 @JvmField public val Right: Vector2 = Vector2(x = 1f, y = 0f) 126 127 /** Returns the distance between this vector and the [other] vector. */ 128 @JvmStatic 129 public fun distance(vector1: Vector2, vector2: Vector2): Float = (vector1 - vector2).length 130 131 /** Returns the angle between this vector and the [other] vector. */ 132 @JvmStatic 133 public fun angularDistance(vector1: Vector2, vector2: Vector2): Float { 134 val dot = vector1 dot vector2 135 val magnitude = vector1.length * vector2.length 136 137 if (magnitude < 1e-10f) { 138 return 0.0f 139 } 140 141 // Clamp due to floating point precision errors that could cause dot to be > mag. 142 // Would cause acos to return NaN. 143 val cos = clamp(dot / magnitude, -1.0f, 1.0f) 144 val angleRadians = acos(cos) 145 146 return toDegrees(angleRadians) 147 } 148 149 /** 150 * Returns a new vector that is linearly interpolated between [start] and [end] using the 151 * interpolation amount [ratio]. 152 * 153 * If [ratio] is outside of the range `[0, 1]`, the returned vector will be extrapolated. 154 */ 155 @JvmStatic 156 public fun lerp(start: Vector2, end: Vector2, ratio: Float): Vector2 = 157 Vector2(lerp(start.x, end.x, ratio), lerp(start.y, end.y, ratio)) 158 159 /** Returns the absolute values of each component of the vector. */ 160 @JvmStatic public fun abs(vector: Vector2): Vector2 = Vector2(abs(vector.x), abs(vector.y)) 161 } 162 } 163