/* * Copyright (c) 2010-2015 William Bittle http://www.dyn4j.org/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of conditions * and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions * and the following disclaimer in the documentation and/or other materials provided with the * distribution. * * Neither the name of dyn4j nor the names of its contributors may be used to endorse or * promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Kotlin translated and modified code Copyright (c) 2016 Minjaesong (CuriousTorvald). */ package org.dyn4j.geometry import org.dyn4j.Epsilon import kotlin.math.sign /** * This class represents a vector or point in 2D space. * * * The operations [Vector2.setMagnitude], [Vector2.getNormalized], * [Vector2.project], and [Vector2.normalize] require the [Vector2] * to be non-zero in length. * * =============================================================================== * * In this Kotlin code, you can use regular operators like + - * /. * * |operator |function | * |-----------|---------------------| * |a + b |Vector2(a).plus(b) | * |a - b |Vector2(a).minus(b) | * |a * b |Vector2(a).times(b) | * |a / b |Vector2(a).div(b) | * |a += b |a.plusAssign(b) | * |a -= b |a.minusAssign(b) | * |a *= b |a.timesAssign(b) | * |a /= b |a.divAssign(b) | * |a dot b |Vector2(a).dot(b) | * |a cross b |Vector2(a).cross(b) | * |!a |this.negative | * |a rotate th|Vector2(a).rotate(th)| * |a to b |Vector2(a).to(b) | * * @author William Bittle and Minjaesong (CuriousTorvald) * * * @version 3.1.11 * * * @since 1.0.0 */ class Vector2 { /** The magnitude of the x component of this [Vector2] */ var x: Double = 0.0 /** The magnitude of the y component of this [Vector2] */ var y: Double = 0.0 /** Default constructor. */ constructor() { } /** * Copy constructor. * @param vector the [Vector2] to copy from */ constructor(vector: Vector2) { this.x = vector.x this.y = vector.y } /** * Optional constructor. * @param x the x component * * * @param y the y component */ constructor(x: Double, y: Double) { this.x = x this.y = y } /** * Creates a [Vector2] from the first point to the second point. * @param x1 the x coordinate of the first point * * * @param y1 the y coordinate of the first point * * * @param x2 the x coordinate of the second point * * * @param y2 the y coordinate of the second point */ constructor(x1: Double, y1: Double, x2: Double, y2: Double) { this.x = x2 - x1 this.y = y2 - y1 } /** * Creates a [Vector2] from the first point to the second point. * @param p1 the first point * * * @param p2 the second point */ constructor(p1: Vector2, p2: Vector2) { this.x = p2.x - p1.x this.y = p2.y - p1.y } /** * Creates a unit length vector in the given direction. * @param direction the direction in radians * * * @since 3.0.1 */ constructor(direction: Double) { this.x = Math.cos(direction) this.y = Math.sin(direction) } operator fun component1() = x operator fun component2() = y /** * Returns a copy of this [Vector2]. * @return [Vector2] */ fun copy(): Vector2 { return Vector2(this.x, this.y) } /** * Returns the distance from this point to the given point. * @param x the x coordinate of the point * * * @param y the y coordinate of the point * * * @return double */ fun distance(x: Double, y: Double): Double { //return Math.hypot(this.x - x, this.y - y); val dx = this.x - x val dy = this.y - y return Math.sqrt(dx * dx + dy * dy) } /** * Returns the distance from this point to the given point. * @param point the point * * * @return double */ fun distance(point: Vector2): Double { //return Math.hypot(this.x - point.x, this.y - point.y); val dx = this.x - point.x val dy = this.y - point.y return Math.sqrt(dx * dx + dy * dy) } /** * Returns the distance from this point to the given point squared. * @param x the x coordinate of the point * * * @param y the y coordinate of the point * * * @return double */ fun distanceSquared(x: Double, y: Double): Double { //return (this.x - x) * (this.x - x) + (this.y - y) * (this.y - y); val dx = this.x - x val dy = this.y - y return dx * dx + dy * dy } /** * Returns the distance from this point to the given point squared. * @param point the point * * * @return double */ fun distanceSquared(point: Vector2): Double { //return (this.x - point.x) * (this.x - point.x) + (this.y - point.y) * (this.y - point.y); val dx = this.x - point.x val dy = this.y - point.y return dx * dx + dy * dy } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ override fun hashCode(): Int { val prime = 31 var result = 1 var temp: Long temp = java.lang.Double.doubleToLongBits(x) result = prime * result + (temp xor temp.ushr(32)).toInt() temp = java.lang.Double.doubleToLongBits(y) result = prime * result + (temp xor temp.ushr(32)).toInt() return result } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ override fun equals(obj: Any?): Boolean { if (obj == null) return false if (obj === this) return true if (obj is Vector2) { return this.x == obj.x && this.y == obj.y } return false } /** * Returns true if the x and y components of this [Vector2] * are the same as the given [Vector2]. * @param vector the [Vector2] to compare to * * * @return boolean */ fun equals(vector: Vector2?): Boolean { if (vector == null) return false if (this === vector) { return true } else { return this.x == vector.x && this.y == vector.y } } /** * Returns true if the x and y components of this [Vector2] * are the same as the given x and y components. * @param x the x coordinate of the [Vector2] to compare to * * * @param y the y coordinate of the [Vector2] to compare to * * * @return boolean */ fun equals(x: Double, y: Double): Boolean { return this.x == x && this.y == y } /* (non-Javadoc) * @see java.lang.Object#toString() */ override fun toString(): String { val sb = StringBuilder() sb.append("(").append(this.x).append(", ").append(this.y).append(")") return sb.toString() } /** * Sets this [Vector2] to the given [Vector2]. * @param vector the [Vector2] to set this [Vector2] to * * * @return [Vector2] this vector */ fun set(vector: Vector2) { this.x = vector.x this.y = vector.y } /** * Sets this [Vector2] to the given [Vector2]. * @param x the x component of the [Vector2] to set this [Vector2] to * * * @param y the y component of the [Vector2] to set this [Vector2] to * * * @return [Vector2] this vector */ fun set(x: Double, y: Double) { this.x = x this.y = y } fun setPolar(magnitude: Double, direction: Double) { this.x = Math.cos(direction) * magnitude this.y = Math.sin(direction) * magnitude } /** * Returns the x component of this [Vector2]. * @return [Vector2] */ val xComponent: Vector2 get() = Vector2(this.x, 0.0) /** * Returns the y component of this [Vector2]. * @return [Vector2] */ val yComponent: Vector2 get() = Vector2(0.0, this.y) /** * Returns the magnitude of this [Vector2]. * @return double */ // the magnitude is just the pathagorean theorem val magnitude: Double get() = Math.sqrt(this.x * this.x + this.y * this.y) /** * Returns the magnitude of this [Vector2] squared. * @return double */ val magnitudeSquared: Double get() = this.x * this.x + this.y * this.y /** * Sets the magnitude of the [Vector2]. * @param magnitude the magnitude * * * @return [Vector2] this vector */ fun setMagnitude(magnitude: Double): Vector2 { // check the given magnitude if (Math.abs(magnitude) <= Epsilon.E) { return Vector2(0.0, 0.0) } // is this vector a zero vector? if (this.isZero) { return Vector2(0.0, 0.0) } // get the magnitude var mag = Math.sqrt(this.x * this.x + this.y * this.y) // normalize and multiply by the new magnitude mag = magnitude / mag val newX = this.x * mag val newY = this.y * mag return Vector2(newX, newY) } /** * Returns the direction of this [Vector2] * as an angle in radians. * @return double angle in radians [-, ] */ val direction: Double get() = Math.atan2(this.y, this.x) /** * Sets the direction of this [Vector2]. * @param angle angle in radians * * * @return [Vector2] this vector */ fun setDirection(angle: Double): Vector2 { //double magnitude = Math.hypot(this.x, this.y); val magnitude = Math.sqrt(this.x * this.x + this.y * this.y) val newX = magnitude * Math.cos(angle) val newY = magnitude * Math.sin(angle) return Vector2(newX, newY) } /** * Adds the given [Vector2] to this [Vector2]. * @param vector the [Vector2] * * * @return [Vector2] this vector */ operator fun plus(vector: Vector2?): Vector2 { return Vector2(this.x + (vector?.x ?: 0.0), this.y + (vector?.y ?: 0.0)) } operator fun plusAssign(vector: Vector2) { this.x += vector.x this.y += vector.y } /** * Subtracts the given [Vector2] from this [Vector2]. * @param vector the [Vector2] * * * @return [Vector2] this vector */ operator fun minus(vector: Vector2): Vector2 { return Vector2(this.x - vector.x, this.y - vector.y) } operator fun minusAssign(vector: Vector2) { this.x -= vector.x this.y -= vector.y } /** * Creates a [Vector2] from this [Vector2] to the given [Vector2]. * @param other : the other [Vector2] * * * @return [Vector2] */ infix fun to(other: Vector2): Vector2 { return Vector2(other.x - this.x, other.y - this.y) } /** * Get product of this vector. * @param scalar the scalar * * * @return [Vector2] this vector */ operator fun times(scalar: Double): Vector2 { return product(scalar) } operator fun timesAssign(scalar: Double) { this.x *= scalar this.y *= scalar } /** * Multiplies this [Vector2] by the given scalar. * @param scalar the scalar * * * @return [Vector2] this vector */ operator fun div(scalar: Double): Vector2 { return Vector2(this.x / scalar, this.y / scalar) } operator fun divAssign(scalar: Double) { this.x /= scalar this.y /= scalar } /** * Multiplies this [Vector2] by the given scalar returning * a new [Vector2] containing the result. * @param scalar the scalar * * * @return [Vector2] */ infix fun product(scalar: Double): Vector2 { return Vector2(this.x * scalar, this.y * scalar) } /** * Returns the dot product of the given [Vector2] * and this [Vector2]. * @param vector the [Vector2] * * * @return double */ infix fun dot(vector: Vector2): Double { return this.x * vector.x + this.y * vector.y } /** * Returns the dot product of the given [Vector2] * and this [Vector2]. * @param x the x component of the [Vector2] * * * @param y the y component of the [Vector2] * * * @return double */ fun dot(x: Double, y: Double): Double { return this.x * x + this.y * y } /** * Returns the cross product of the this [Vector2] and the given [Vector2]. * @param vector the [Vector2] * * * @return double */ infix fun cross(vector: Vector2): Double { return this.x * vector.y - this.y * vector.x } /** * Returns the cross product of the this [Vector2] and the given [Vector2]. * @param x the x component of the [Vector2] * * * @param y the y component of the [Vector2] * * * @return double */ fun cross(x: Double, y: Double): Double { return this.x * y - this.y * x } /** * Returns the cross product of this [Vector2] and the z value of the right [Vector2]. * @param z the z component of the [Vector2] * * * @return [Vector2] */ infix fun cross(z: Double): Vector2 { return Vector2(-1.0 * this.y * z, this.x * z) } /** * Returns true if the given [Vector2] is orthogonal (perpendicular) * to this [Vector2]. * * * If the dot product of this vector and the given vector is * zero then we know that they are perpendicular * @param vector the [Vector2] * * * @return boolean */ fun isOrthogonal(vector: Vector2): Boolean { return if (Math.abs(this.x * vector.x + this.y * vector.y) <= Epsilon.E) true else false } /** * Returns true if the given [Vector2] is orthogonal (perpendicular) * to this [Vector2]. * * * If the dot product of this vector and the given vector is * zero then we know that they are perpendicular * @param x the x component of the [Vector2] * * * @param y the y component of the [Vector2] * * * @return boolean */ fun isOrthogonal(x: Double, y: Double): Boolean { return if (Math.abs(this.x * x + this.y * y) <= Epsilon.E) true else false } /** * Returns true if this [Vector2] is the zero [Vector2]. * @return boolean */ val isZero: Boolean get() = Math.abs(this.x) <= Epsilon.E && Math.abs(this.y) <= Epsilon.E /** * Returns false if this [Vector2] is the zero [Vector2]. * @return boolean */ val isNotZero: Boolean get() = !isZero /** * Negates this [Vector2]. * @return [Vector2] this vector */ operator fun not() = this.negative /** * Negates this [Vector2]. * @return [Vector2] this vector */ operator fun unaryMinus() = this.negative /** * Returns a [Vector2] which is the negative of this [Vector2]. * @return [Vector2] */ val negative: Vector2 get() = Vector2(-this.x, -this.y) /** * Sets the [Vector2] to the zero [Vector2] * @return [Vector2] this vector */ fun zero() { this.x = 0.0 this.y = 0.0 } /** * Rotates about the origin. * @param theta the rotation angle in radians * * * @return [Vector2] this vector */ infix fun rotate(theta: Double): Vector2 { val cos = Math.cos(theta) val sin = Math.sin(theta) val x = this.x val y = this.y val newX = x * cos - y * sin val newY = x * sin + y * cos return Vector2(newX, newY) } /** * Projects this [Vector2] onto the given [Vector2]. * @param vector the [Vector2] * * * @return [Vector2] the projected [Vector2] */ fun project(vector: Vector2): Vector2 { val dotProd = this.dot(vector) var denominator = vector.dot(vector) if (denominator <= Epsilon.E) return Vector2() denominator = dotProd / denominator return Vector2(denominator * vector.x, denominator * vector.y) } /** * Returns the right-handed normal of this vector. * @return [Vector2] the right hand orthogonal [Vector2] */ val rightHandOrthogonalVector: Vector2 get() = Vector2(-this.y, this.x) /** * Sets this vector to the right-handed normal of this vector. * @return [Vector2] this vector * * * @see .getRightHandOrthogonalVector */ fun right(): Vector2 { val temp = this.x val newX = -this.y val newY = temp return Vector2(newX, newY) } /** * Returns the left-handed normal of this vector. * @return [Vector2] the left hand orthogonal [Vector2] */ val leftHandOrthogonalVector: Vector2 get() = Vector2(this.y, -this.x) /** * Sets this vector to the left-handed normal of this vector. * @return [Vector2] this vector * * * @see .getLeftHandOrthogonalVector */ fun left(): Vector2 { val temp = this.x val newX = this.y val newY = -temp return Vector2(newX, newY) } /** * Returns a unit [Vector2] of this [Vector2]. * * * This method requires the length of this [Vector2] is not zero. * @return [Vector2] */ val normalized: Vector2 get() { var magnitude = this.magnitude if (magnitude <= Epsilon.E) return Vector2() magnitude = 1.0 / magnitude return Vector2(this.x * magnitude, this.y * magnitude) } /** * Converts this [Vector2] into a unit [Vector2] and returns * the magnitude before normalization. * * * This method requires the length of this [Vector2] is not zero. * @return double */ fun normalize(): Double { val magnitude = Math.sqrt(this.x * this.x + this.y * this.y) if (magnitude <= Epsilon.E) return 0.0 val m = 1.0 / magnitude this.x *= m this.y *= m //return 1.0 / m; return magnitude } /** * Returns the smallest angle between the given [Vector2]s. * * * Returns the angle in radians in the range - to . * @param vector the [Vector2] * * * @return angle in radians [-, ] */ fun getAngleBetween(vector: Vector2): Double { val a = Math.atan2(vector.y, vector.x) - Math.atan2(this.y, this.x) if (a > Math.PI) return a - 2.0 * Math.PI if (a < -Math.PI) return a + 2.0 * Math.PI return a } val signum: Vector2 get() = Vector2(this.x.sign, this.y.sign) companion object { /** A vector representing the x-axis; this vector should not be changed at runtime; used internally */ internal val X_AXIS = Vector2(1.0, 0.0) /** A vector representing the y-axis; this vector should not be changed at runtime; used internally */ internal val Y_AXIS = Vector2(0.0, 1.0) /** * Returns a new [Vector2] given the magnitude and direction. * @param magnitude the magnitude of the [Vector2] * * * @param direction the direction of the [Vector2] in radians * * * @return [Vector2] */ fun create(magnitude: Double, direction: Double): Vector2 { val x = magnitude * Math.cos(direction) val y = magnitude * Math.sin(direction) return Vector2(x, y) } /** * The triple product of [Vector2]s is defined as: * * a x (b x c) * * However, this method performs the following triple product: * * (a x b) x c * * this can be simplified to: * * -a * (b c) + b * (a c) * * or: * * b * (a c) - a * (b c) * * @param a the a [Vector2] in the above equation * * * @param b the b [Vector2] in the above equation * * * @param c the c [Vector2] in the above equation * * * @return [Vector2] */ fun tripleProduct(a: Vector2, b: Vector2, c: Vector2): Vector2 { // expanded version of above formula val r = Vector2() // perform a.dot(c) val ac = a.x * c.x + a.y * c.y // perform b.dot(c) val bc = b.x * c.x + b.y * c.y // perform b * a.dot(c) - a * b.dot(c) r.x = b.x * ac - a.x * bc r.y = b.y * ac - a.y * bc return r } } }