All Downloads are FREE. Search and download functionalities are using the official Maven repository.

godot.core.Vector2.kt Maven / Gradle / Ivy

package godot.core

import godot.util.*
import kotlin.math.*

@Suppress("MemberVisibilityCanBePrivate")
class Vector2(
    var x: RealT,
    var y: RealT
) : Comparable, CoreType {

    //CONSTANTS
    enum class Axis(val value: Int) {
        X(0),
        Y(1);

        companion object {
            fun from(value: Int) = when (value) {
                0 -> X
                1 -> Y
                else -> throw AssertionError("Unknown axis for Vector2: $value")
            }
        }
    }

    companion object {
        val AXIS_X = Axis.X.value
        val AXIS_Y = Axis.Y.value
        val ZERO: Vector2
            get() = Vector2(0, 0)
        val ONE: Vector2
            get() = Vector2(1, 1)
        val INF: Vector2
            get() = Vector2(RealT.POSITIVE_INFINITY, RealT.POSITIVE_INFINITY)
        val LEFT: Vector2
            get() = Vector2(-1, 0)
        val RIGHT: Vector2
            get() = Vector2(1, 0)
        val UP: Vector2
            get() = Vector2(0, -1)
        val DOWN: Vector2
            get() = Vector2(0, 1)
    }


    //CONSTRUCTOR
    constructor() :
        this(0.0, 0.0)

    constructor(vec: Vector2) :
        this(vec.x, vec.y)

    constructor(other: Vector2i) : this(other.x, other.y)

    constructor(x: Number, y: Number) :
        this(x.toRealT(), y.toRealT())


    //API
    /**
     * Returns a new vector with all components in absolute values (i.e. positive).
     */
    fun abs(): Vector2 {
        return Vector2(abs(x), abs(y))
    }

    /**
     * Returns the vector’s angle in radians with respect to the x-axis, or (1, 0) vector.
     * Equivalent to the result of atan2 when called with the vector’s x and y as parameters: atan2(x, y).
     */
    fun angle(): RealT {
        return atan2(y, x)
    }

    /**
     * Returns the angle in radians between the two vectors.
     */
    fun angleTo(to: Vector2): RealT {
        return atan2(cross(to), dot(to))
    }

    /**
     * Returns the angle in radians between the line connecting the two points and the x coordinate.
     */
    fun angleToPoint(other: Vector2): RealT {
        return atan2(y - other.y, x - other.x)
    }

    /**
     * Returns the ratio of x to y.
     */
    fun aspect(): RealT {
        return this.x / this.y
    }

    /**
     * Returns the derivative at the given [t] on the Bézier curve defined by this vector and the given [control1],
     * [control2], and [end] points.
     */
    fun bezierDerivative(
        control1: Vector2,
        control2: Vector2,
        end: Vector2,
        t: RealT
    ) = Vector2(
        bezierDerivative(this.x, control1.x, control2.x, end.x, t),
        bezierDerivative(this.y, control1.y, control2.y, end.y, t)
    )

    /**
     * Returns the point at the given [t] on the Bézier curve defined by this vector and the given [control1],
     * [control2], and [end] points.
     */
    fun bezierInterpolate(
        control1: Vector2,
        control2: Vector2,
        end: Vector2,
        t: RealT
    ) = Vector2(
        bezierInterpolate(this.x, control1.x, control2.x, end.x, t),
        bezierInterpolate(this.y, control1.y, control2.y, end.y, t)
    )

    /**
     * Returns the vector “bounced off” from a plane defined by the given normal.
     */
    fun bounce(n: Vector2): Vector2 {
        return -reflect(n)
    }

    /**
     * Returns the vector with all components rounded up.
     */
    fun ceil(): Vector2 {
        return Vector2(ceil(x), ceil(y))
    }

    /**
     * Returns a new vector with all components clamped between the components of min and max, by running
     * @GlobalScope.clamp on each component.
     */
    fun clamp(min: Vector2, max: Vector2) = Vector2(x.coerceIn(min.x, max.x), y.coerceIn(min.y, max.y))

    /**
     * Returns the 2 dimensional analog of the cross product with the given vector.
     */
    fun cross(other: Vector2): RealT {
        return x * other.y - y * other.x
    }

    /**
     * Cubicly interpolates between this vector and b using pre_a and post_b as handles, and returns the result at position t.
     * t is in the range of 0.0 - 1.0, representing the amount of interpolation.
     */
    fun cubicInterpolate(v: Vector2, pre: Vector2, post: Vector2, t: RealT): Vector2 {
        val p0: Vector2 = pre
        val p1: Vector2 = this
        val p2: Vector2 = v
        val p3: Vector2 = post

        val t2: RealT = t * t
        val t3: RealT = t2 * t

        return ((p1 * 2.0) +
            (-p0 + p2) * t +
            (p0 * 2.0 - p1 * 5.0 + p2 * 4.0 - p3) * t2 +
            (-p0 + p1 * 3.0 - p2 * 3.0 + p3) * t3) * 0.5
    }

    /**
     * Cubically interpolates between this vector and b using pre_a and post_b as handles, and returns the result at
     * position weight. weight is on the range of 0.0 to 1.0, representing the amount of interpolation.
     */
    fun cubicInterpolateInTime(
        b: Vector2,
        preA: Vector2,
        postB: Vector2,
        weight: RealT,
        bT: RealT,
        preAT: RealT,
        postBT: RealT
    ) = Vector2(this).also {
        it.x = cubicInterpolateInTime(
            it.x,
            b.x,
            preA.x,
            postB.x,
            weight,
            bT,
            preAT,
            postBT
        )
        it.y = cubicInterpolateInTime(
            it.y,
            b.y,
            preA.y,
            postB.y,
            weight,
            bT,
            preAT,
            postBT
        )
    }

    /**
     * Returns the normalized vector pointing from this vector to b.
     */
    fun directionTo(other: Vector2): Vector2 {
        val ret = Vector2(other.x - x, other.y - y)
        ret.normalize()
        return ret
    }

    /**
     * Returns the squared distance to vector b.
     * Prefer this function over distance_to if you need to sort vectors or need the squared distance for some formula.
     */
    fun distanceSquaredTo(other: Vector2): RealT {
        return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y)
    }

    /**
     * Returns the distance to vector b.
     */
    fun distanceTo(other: Vector2): RealT {
        return sqrt((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y))
    }

    /**
     * Returns the dot product with vector b.
     */
    fun dot(other: Vector2): RealT {
        return x * other.x + y * other.y
    }

    /**
     * Returns the vector with all components rounded down.
     */
    fun floor(): Vector2 {
        return Vector2(floor(x), floor(y))
    }

    /**
     * Creates a unit Vector2 rotated to the given angle in radians. This is equivalent to doing
     * Vector2(cos(angle), sin(angle)) or Vector2.RIGHT.rotated(angle).
     */
    fun fromAngle(angle: RealT) = Vector2(cos(angle), sin(angle))

    /**
     * Returns true if this vector and v are approximately equal, by running isEqualApprox on each component.
     */
    fun isEqualApprox(other: Vector2): Boolean {
        return isEqualApprox(
            other.x,
            x
        ) && isEqualApprox(other.y, y)
    }

    /**
     * Returns true if this vector is finite, by calling @GlobalScope.is_finite on each component.
     */
    fun isFinite() = x.isFinite() && y.isFinite()

    /**
     * Returns true if the vector is normalized.
     */
    fun isNormalized(): Boolean {
        return isEqualApprox(this.length(), 1.0)
    }

    /**
     * Returns true if this vector's values are approximately zero
     */
    fun isZeroApprox() = isEqualApprox(Vector2.ZERO)

    /**
     * Returns the vector’s length.
     */
    fun length(): RealT {
        return sqrt(x * x + y * y)
    }

    /**
     * Returns the vector’s length squared.
     * Prefer this method over length if you need to sort vectors or need the squared length for some formula.
     */
    fun lengthSquared(): RealT {
        return x * x + y * y
    }

    /**
     * Returns the result of the linear interpolation between this vector and to by amount weight. weight is on the
     * range of 0.0 to 1.0, representing the amount of interpolation.
     */
    fun lerp(to: Vector2, weight: RealT) = Vector2(
        x + (weight * (to.x - x)),
        y + (weight * (to.y - y))
    )

    /**
     * Returns the vector with a maximum length by limiting its length to length.
     */
    fun limitLenght(length: RealT = 1.0): Vector2 {
        val l = length()
        var v = Vector2(this)
        if (l > 0 && length < l) {
            v /= l
            v *= length
        }

        return v
    }

    /**
     * Returns the axis of the vector's highest value. See AXIS_* constants. If all components are equal,
     * this method returns AXIS_X.
     */
    fun maxAxisIndex() = if (x < y) AXIS_Y else AXIS_X

    /**
     * Returns the axis of the vector's lowest value. See AXIS_* constants. If all components are equal,
     * this method returns AXIS_Y.
     */
    fun minAxisIndex() = if (x < y) AXIS_X else AXIS_Y

    /**
     * Moves the vector toward to by the fixed delta amount.
     */
    fun moveToward(to: Vector2, delta: RealT): Vector2 {
        val vd = to - this
        val len = vd.length()
        return if (len <= delta || len < CMP_EPSILON)
            to
        else
            this + vd / len * delta
    }

    /**
     * Returns the vector scaled to unit length. Equivalent to v / v.length().
     */
    fun normalized(): Vector2 {
        val v: Vector2 = Vector2(this)

        v.normalize()
        return v
    }

    internal fun normalize() {
        val l: RealT = length()
        if (isEqualApprox(l, 0.0)) {
            x = 0.0
            y = 0.0
        } else {
            x /= l
            y /= l
        }
    }

    /**
     * Returns a perpendicular vector rotated 90 degrees counter-clockwise compared to the original,
     * with the same length.
     */
    fun orthogonal() = Vector2(y, -x)

    /**
     * Returns a vector composed of the fposmod of this vector’s components and mod.
     */
    fun posmod(mod: RealT): Vector2 {
        return Vector2(x.rem(mod), y.rem(mod))
    }

    /**
     * Returns a vector composed of the fposmod of this vector’s components and modv’s components.
     */
    fun posmodv(modv: Vector2): Vector2 {
        return Vector2(x.rem(modv.x), y.rem(modv.y))
    }

    /**
     * Returns the vector projected onto the vector b.
     */
    fun project(vec: Vector2): Vector2 {
        val v1: Vector2 = vec
        val v2: Vector2 = this
        return v2 * (v1.dot(v2) / v2.dot(v2))
    }

    /**
     * Returns the vector reflected from a plane defined by the given normal.
     */
    fun reflect(vec: Vector2): Vector2 {
        return vec * this.dot(vec) * 2.0 - this
    }

    /**
     * Returns the vector rotated by phi radians.
     */
    fun rotated(by: RealT): Vector2 {
        var v = Vector2(0.0, 0.0)
        v.rotate(this.angle() + by)
        v *= length()
        return v
    }

    internal fun rotate(radians: RealT) {
        x = cos(radians)
        y = sin(radians)
    }

    /**
     * Returns the vector with all components rounded to the nearest integer, with halfway cases rounded away from zero.
     */
    fun round(): Vector2 {
        return Vector2(round(x), round(y))
    }

    /**
     * Returns the vector with each component set to one or negative one, depending on the signs of the components.
     */
    fun sign(): Vector2 {
        return Vector2(sign(x), sign(y))
    }

    /**
     * Returns the result of spherical linear interpolation between this vector and b, by amount t.
     * t is in the range of 0.0 - 1.0, representing the amount of interpolation.
     *
     * Note: Both vectors must be normalized.
     */
    fun slerp(b: Vector2, t: RealT): Vector2 {
        require(this.isNormalized() && b.isNormalized()) { "Both this and b vector must be normalized!" }
        val theta: RealT = angleTo(b)
        return rotated((theta * t))
    }

    /**
     * Returns the component of the vector along a plane defined by the given normal.
     */
    fun slide(vec: Vector2): Vector2 {
        return vec - this * this.dot(vec)
    }

    /**
     * Returns the vector snapped to a grid with the given size.
     */
    fun snapped(by: Vector2): Vector2 {
        val newX = if (isEqualApprox(by.x, 0.0)) {
            floor(x / by.x + 0.5).toRealT()
        } else {
            x
        }
        val newY = if (isEqualApprox(by.x, 0.0)) {
            floor(y / by.y + 0.5).toRealT()
        } else {
            y
        }
        return Vector2(newX, newY)
    }

    operator fun get(idx: Int): RealT =
        when (idx) {
            0 -> x
            1 -> y
            else -> throw IndexOutOfBoundsException()
        }

    operator fun set(n: Int, f: RealT) =
        when (n) {
            0 -> x = f
            1 -> y = f
            else -> throw IndexOutOfBoundsException()
        }

    operator fun plus(v: Vector2) = Vector2(x + v.x, y + v.y)
    operator fun plus(scalar: Int) = Vector2(x + scalar, y + scalar)
    operator fun plus(scalar: Long) = Vector2(x + scalar, y + scalar)
    operator fun plus(scalar: Float) = Vector2(x + scalar, y + scalar)
    operator fun plus(scalar: Double) = Vector2(x + scalar, y + scalar)

    operator fun minus(v: Vector2) = Vector2(x - v.x, y - v.y)
    operator fun minus(scalar: Int) = Vector2(x - scalar, y - scalar)
    operator fun minus(scalar: Long) = Vector2(x - scalar, y - scalar)
    operator fun minus(scalar: Float) = Vector2(x - scalar, y - scalar)
    operator fun minus(scalar: Double) = Vector2(x - scalar, y - scalar)

    operator fun times(v1: Vector2) = Vector2(x * v1.x, y * v1.y)
    operator fun times(scalar: Int) = Vector2(x * scalar, y * scalar)
    operator fun times(scalar: Long) = Vector2(x * scalar, y * scalar)
    operator fun times(scalar: Float) = Vector2(x * scalar, y * scalar)
    operator fun times(scalar: Double) = Vector2(x * scalar, y * scalar)

    operator fun div(v1: Vector2) = Vector2(x / v1.x, y / v1.y)
    operator fun div(scalar: Int) = Vector2(x / scalar, y / scalar)
    operator fun div(scalar: Long) = Vector2(x / scalar, y / scalar)
    operator fun div(scalar: Float) = Vector2(x / scalar, y / scalar)
    operator fun div(scalar: Double) = Vector2(x / scalar, y / scalar)

    operator fun unaryMinus() = Vector2(-x, -y)

    fun toVector2i() = Vector2i(x.toInt(), x.toInt())

    override fun equals(other: Any?): Boolean =
        when (other) {
            is Vector2 -> (isEqualApprox(x, other.x) && isEqualApprox(y, other.y))
            else -> false
        }

    override fun compareTo(other: Vector2): Int =
        if (isEqualApprox(x, other.x)) {
            when {
                y < other.y -> -1
                isEqualApprox(y, other.y) -> 0
                else -> 1
            }
        } else {
            when {
                x < other.x -> -1
                else -> 1
            }
        }

    override fun hashCode(): Int {
        var result = x.hashCode()
        result = 31 * result + y.hashCode()
        return result
    }

    override fun toString(): String {
        return "($x, $y)"
    }
}

operator fun Int.plus(vec: Vector2) = vec + this
operator fun Long.plus(vec: Vector2) = vec + this
operator fun Float.plus(vec: Vector2) = vec + this
operator fun Double.plus(vec: Vector2) = vec + this

operator fun Int.minus(vec: Vector2) = Vector2(this - vec.x, this - vec.y)
operator fun Long.minus(vec: Vector2) = Vector2(this - vec.x, this - vec.y)
operator fun Float.minus(vec: Vector2) = Vector2(this - vec.x, this - vec.y)
operator fun Double.minus(vec: Vector2) = Vector2(this - vec.x, this - vec.y)

operator fun Int.times(vec: Vector2) = vec * this
operator fun Long.times(vec: Vector2) = vec * this
operator fun Float.times(vec: Vector2) = vec * this
operator fun Double.times(vec: Vector2) = vec * this




© 2015 - 2025 Weber Informatics LLC | Privacy Policy