DS-Pokemon-Rom-Editor/Ekona/Mathematics/Vector3.cs
AdAstra-LD 73ca3d06a9 Added Ekona and NImages file support
More progress with the Trainer Editor
2021-07-21 23:04:53 +02:00

887 lines
28 KiB
C#

// -----------------------------------------------------------------------
// <copyright file="Vector3.cs" company="NII">
//
// Copyright (C) 2016 MetLob
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// </copyright>
// -----------------------------------------------------------------------
namespace Ekona.Mathematics
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// Vector 3D
/// </summary>
public struct Vector3
{
#region Fields
double x;
double y;
double z;
#endregion
#region Initialize
/// <summary>
/// Initialize the vector by coordinates
/// </summary>
/// <param name="x">Abscissa</param>
/// <param name="y">Ordinate</param>
/// <param name="z">Applicate</param>
public Vector3(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
/// <summary>
/// Initialize the vector by coordinates
/// </summary>
/// <param name="coordinates">Array of coodinates</param>
public Vector3(double[] coordinates)
{
this.x = coordinates[0];
this.y = coordinates[1];
this.z = coordinates[2];
}
/// <summary>
/// Initialize the vector as copy
/// </summary>
/// <param name="vector">Source vector</param>
public Vector3(Vector3 vector)
{
x = vector.X;
y = vector.Y;
z = vector.Z;
}
#endregion
#region Contants
/// <summary>
/// Null-vector
/// </summary>
public static Vector3 Zero
{
get { return new Vector3(0.0, 0.0, 0.0); }
}
/// <summary>
/// X-axis
/// </summary>
public static Vector3 XAxis
{
get { return new Vector3(1.0, 0.0, 0.0); }
}
/// <summary>
/// Y-axis
/// </summary>
public static Vector3 YAxis
{
get { return new Vector3(0.0, 1.0, 0.0); }
}
/// <summary>
/// Z-axis
/// </summary>
public static Vector3 ZAxis
{
get { return new Vector3(0.0, 0.0, 1.0); }
}
#endregion
#region Property
/// <summary>
/// Get or set the x coorinate (abscissa of the vector)
/// </summary>
/// <value>The abscissa of the vector</value>
public double X
{
get { return x; }
set { x = value; }
}
/// <summary>
/// Get or set the x coorinate (ordinate of the vector)
/// </summary>
/// <value>The ordinate of the vector</value>
public double Y
{
get { return y; }
set { y = value; }
}
/// <summary>
/// Get or set the x coorinate (applicate of the vector)
/// </summary>
/// <value>The applicate of the vector</value>
public double Z
{
get { return z; }
set { z = value; }
}
#endregion
#region Static methods
/// <summary>
/// Addition of two vectors Vector3
/// </summary>
/// <param name="v">First summand Vector3</param>
/// <param name="w">Second summand Vector3</param>
/// <returns>The result sum vector = v + w</returns>
public static Vector3 Add(Vector3 v, Vector3 w)
{
return new Vector3(v.X + w.X, v.Y + w.Y, v.Z + w.Z);
}
/// <summary>
/// Addition vector and scalar
/// </summary>
/// <param name="v">First summand Vector3</param>
/// <param name="s">Second scalar summand double</param>
/// <returns>The result sum vector = v + s</returns>
public static Vector3 Add(Vector3 v, double s)
{
return new Vector3(v.X + s, v.Y + s, v.Z + s);
}
/// <summary>
/// The subtract of two vectors
/// </summary>
/// <param name="v">Reduces vector</param>
/// <param name="w">Subtracts the vector</param>
/// <returns>The subtract of two vectors</returns>
/// <remarks>
/// result[i] = v[i] - w[i].
/// </remarks>
public static Vector3 Subtract(Vector3 v, Vector3 w)
{
return new Vector3(v.X - w.X, v.Y - w.Y, v.Z - w.Z);
}
/// <summary>
/// Subtraction scalar from a vector
/// </summary>
/// <param name="v">Reduces vector</param>
/// <param name="s">Subtracts the scalar</param>
/// <returns>The difference vector and scalar</returns>
/// <remarks>
/// result[i] = v[i] - s
/// </remarks>
public static Vector3 Subtract(Vector3 v, double s)
{
return new Vector3(v.X - s, v.Y - s, v.Z - s);
}
/// <summary>
/// Subtraction vector from a scalar
/// </summary>
/// <param name="s">Reduces the scalar (essentially vector (s,s,s))</param>
/// <param name="v">Subtracts the vector</param>
/// <returns>The difference of the scalar and vector</returns>
/// <remarks>
/// result[i] = s - v[i]
/// </remarks>
public static Vector3 Subtract(double s, Vector3 v)
{
return new Vector3(s - v.X, s - v.Y, s - v.Z);
}
/// <summary>
/// Dividing a vector to another vector
/// </summary>
/// <param name="u">Divisible vector</param>
/// <param name="v">Vector divider</param>
/// <returns>Quotient vector</returns>
/// <remarks>
/// result[i] = u[i] / v[i].
/// </remarks>
public static Vector3 Divide(Vector3 u, Vector3 v)
{
return new Vector3(u.X / v.X, u.Y / v.Y, u.Z / v.Z);
}
/// <summary>
/// The division of a vector by a scalar
/// </summary>
/// <param name="v">Divisible vector</param>
/// <param name="s">Divider - scalar</param>
/// <returns>Quotient vector</returns>
/// <remarks>
/// result[i] = v[i] / s;
/// </remarks>
public static Vector3 Divide(Vector3 v, double s)
{
return new Vector3(v.X / s, v.Y / s, v.Z / s);
}
/// <summary>
/// Dividing a scalar to a vector
/// </summary>
/// <param name="s">Divisible scalar - Vector(s,s,s)</param>
/// <param name="v">Vector divider</param>
/// <returns>Quotient vector</returns>
/// <remarks>
/// result[i] = s / v[i]
/// </remarks>
public static Vector3 Divide(double s, Vector3 v)
{
return new Vector3(s / v.X, s / v.Y, s / v.Z);
}
/// <summary>
/// Multiplication of a vector by a scalar
/// </summary>
/// <param name="u">Vector</param>
/// <param name="s">Scalar</param>
/// <returns>The vector containing the product</returns>
public static Vector3 Multiply(Vector3 u, double s)
{
return new Vector3(u.X * s, u.Y * s, u.Z * s);
}
/// <summary>
/// Calculating the scalar product
/// </summary>
/// <param name="u">Vector</param>
/// <param name="v">Vector</param>
/// <returns>Scalar containing the result of the scalar product</returns>
public static double DotProduct(Vector3 u, Vector3 v)
{
return u.DotProduct(v);
}
/// <summary>
/// Calculating the cross product of two vectors
/// </summary>
/// <param name="u">Vector</param>
/// <param name="v">Vector</param>
/// <returns>The vector containing the result of the cross product</returns>
public static Vector3 CrossProduct(Vector3 u, Vector3 v)
{
return new Vector3(
u.Y * v.Z - u.Z * v.Y,
u.Z * v.X - u.X * v.Z,
u.X * v.Y - u.Y * v.X);
}
/// <summary>
/// The negate of the vector - an appeal of its component signs
/// </summary>
/// <param name="v">Source vector</param>
/// <returns>Inverted vector</returns>
public static Vector3 Negate(Vector3 v)
{
return new Vector3(-v.X, -v.Y, -v.Z);
}
/// <summary>
/// Testing for equivalence using the default tolerance
/// </summary>
/// <param name="v">Vector</param>
/// <param name="u">Vector</param>
/// <returns>True if the vector equivalent, otherwise false</returns>
public static bool ApproxEqual(Vector3 v, Vector3 u)
{
return ApproxEqual(v, u, Double.Epsilon);
}
/// <summary>
/// Testing for equivalence
/// </summary>
/// <param name="v">Vector</param>
/// <param name="u">Vector</param>
/// <param name="tolerance">Tolerance (possible deviation)</param>
/// <returns>True if the vector equivalent, otherwise false</returns>
public static bool ApproxEqual(Vector3 v, Vector3 u, double tolerance)
{
return
(
(Math.Abs(v.X - u.X) <= tolerance) &&
(Math.Abs(v.Y - u.Y) <= tolerance) &&
(Math.Abs(v.Z - u.Z) <= tolerance)
);
}
/// <summary>
/// Normalizing the vector
/// </summary>
/// <param name="v">Vector for normalization</param>
/// <returns>Normal vector</returns>
public static Vector3 Normalize(Vector3 v)
{
v.Normalize();
return v;
}
/// <summary>
/// Computation of the angle between the vectors
/// </summary>
/// <param name="vector1">Vector</param>
/// <param name="vector2">Vector</param>
/// <returns>Angle in radians</returns>
public static double Angle(Vector3 vector1, Vector3 vector2)
{
return vector1.Angle(vector2);
}
/// <summary>
/// Angle counterclockwise from the first vector to the second
/// </summary>
/// <param name="beginVector">First vector</param>
/// <param name="endVector">Second vector</param>
/// <returns>Angle in radians</returns>
public static double CounterclockwiseAngleBetween(Vector3 beginVector, Vector3 endVector)
{
// It defines a large or small angle
double factor = beginVector.X * endVector.Y - endVector.X * beginVector.Y;
// Arc angle
double betweenAngle = Vector3.Angle(beginVector, endVector);
// Adjust the angle
betweenAngle = factor > 0 ? betweenAngle : 2.0 * Math.PI - betweenAngle;
return betweenAngle;
}
/// <summary>
/// Angle counterclockwise from the first vector to the second
/// </summary>
/// <param name="firstVector">First vector</param>
/// <param name="secondVector">Second vector</param>
/// <param name="direction">The vector defines the direction of counterclockwise</param>
/// <returns>Угол</returns>
public static double CounterclockwiseAngleBetween(Vector3 firstVector, Vector3 secondVector, Vector3 direction)
{
//Vector3d xVector = new Vector3d(firstVector);
//xVector.Normalize();
//Vector3d zVector = directionAxe;
//zVector.Normalize();
//Vector3d yVector = zVector.CrossProduct(xVector);
//yVector.Normalize();
//// xVector, yVector, zVector образовали правую декартову систему координат
//// нахожу координаты endvector в этой системе
//double x = xVector.DotProduct(secondVector);
//double y = yVector.DotProduct(secondVector);
//double angle = Math.Acos(Math.Abs(x) / secondVector.Length());
//if (x > 0.0d)
//{
// if (y < 0.0d)
// angle = 2 * Math.PI - angle;
//}
//else
// if (y > 0.0d)
// angle = Math.PI - angle;
// else
// angle = Math.PI + angle;
double angle = firstVector.Angle(secondVector);
Vector3 z = Vector3.CrossProduct(firstVector, secondVector);
double det = Vector3.DotProduct(z, direction);
if (det < 0) angle = 2 * Math.PI - angle;
return angle;
}
/// <summary>
/// Get a geometric center of a array of vectors
/// </summary>
/// <param name="points">Array of points</param>
/// <returns>The geometric center</returns>
public static Vector3 GetCenter(Vector3[] points)
{
Vector3 center = new Vector3(0, 0, 0);
foreach (Vector3 point in points)
center = center + point;
return center / points.Length;
}
/// <summary>
/// Changes y and z locations
/// </summary>
/// <param name="vector">The vector whose coordinates change</param>
/// <returns>The vector with modified coordinates</returns>
static public Vector3 SwapYZ(Vector3 vector)
{
return new Vector3(vector.X, vector.Z, vector.Y);
}
#endregion
#region Methods of the vector
/// <summary>
/// Rotation vector (points around the origin) counterclockwise relative to the normal vector
/// </summary>
/// <param name="angle">
/// Angle of rotation
/// </param>
/// <param name="normal">
/// The normal of the rotation plane
/// </param>
/// <returns>
/// Turned vector (point)
/// </returns>
public Vector3 Rotate(double angle, Vector3 normal)
{
// Switching to the normal plane coordinate system
Vector3 axisZ = normal.Unit();
Vector3 axisX = axisZ.Orthogonal();
Vector3 axisY = Vector3.CrossProduct(axisZ, axisX);
Vector3 thisVector = new Vector3(
this.X * axisX.X + this.Y * axisX.Y + this.Z * axisX.Z,
this.X * axisY.X + this.Y * axisY.Y + this.Z * axisY.Z,
this.X * axisZ.X + this.Y * axisZ.Y + this.Z * axisZ.Z);
// Rotating
double c = Math.Cos(angle);
double s = Math.Sin(angle);
Vector3 rotated = new Vector3(
c * thisVector.X - s * thisVector.Y, s * thisVector.X + c * thisVector.Y, thisVector.Z);
// Translate rotated vector back into a common coordinate system
return new Vector3(
axisX.X * rotated.X + axisY.X * rotated.Y + axisZ.X * rotated.Z,
axisX.Y * rotated.X + axisY.Y * rotated.Y + axisZ.Y * rotated.Z,
axisX.Z * rotated.X + axisY.Z * rotated.Y + axisZ.Z * rotated.Z).Unit() * this.Length();
}
/// <summary>
/// Rotation point around a origin point counterclockwise relative to the normal vector
/// </summary>
/// <param name="origin">
/// Origin
/// </param>
/// <param name="angle">
/// Angle of rotation
/// </param>
/// <param name="normal">
/// The normal of the rotation plane
/// </param>
/// <returns>
/// Turned point
/// </returns>
public Vector3 Rotate(Vector3 origin, double angle, Vector3 normal)
{
return origin + (this - origin).Rotate(angle, normal);
}
/// <summary>
/// Normalizing the vector (bringing it to the unit)
/// </summary>
/// <exception cref="System.DivideByZeroException">
/// It occurs when an attempt to normalize the zero vector
/// </exception>
public void Normalize()
{
double length = Length();
if (length == 0.0f)
{
throw new Exception("Trying to normalize a zero vector.");
}
x /= length;
y /= length;
z /= length;
}
/// <summary>
/// Returns a unit vector drawn from this
/// </summary>
/// <returns>The new vector (normalized clone)</returns>
/// <exception cref="System.DivideByZeroException">
/// It occurs when an attempt to normalize the zero vector
/// </exception>
public Vector3 Unit()
{
Vector3 result = new Vector3(this);
if (result == Vector3.Zero) return result;
result.Normalize();
return result;
}
/// <summary>
/// Get An orthogonal vector
/// </summary>
/// <returns>The orthogonal vector</returns>
public Vector3 Orthogonal()
{
Vector3 result = Vector3.Zero;
int maxCoordIndex = 0;
for (int i = 1; i < 3; i++)
if (Math.Abs(this[i]) > Math.Abs(this[maxCoordIndex]))
maxCoordIndex = i;
result[(maxCoordIndex + 1) % 3] = 0;
result[(maxCoordIndex + 2) % 3] = -this[maxCoordIndex];
result[maxCoordIndex] = this[(maxCoordIndex + 2) % 3];
return result.Unit() * this.Length();
}
/// <summary>
/// Get the length of the vector
/// </summary>
/// <returns>The length of the vector (Sqrt(X*X + Y*Y + Z*Z))</returns>
public double Length()
{
double lengthSquared = LengthSquared();
return lengthSquared == 1.0 ? 1.0 : (double)Math.Sqrt(lengthSquared);
}
/// <summary>
/// Get the length squared of the vector
/// </summary>
/// <returns>The length squared (X*X + Y*Y + Z*Z)</returns>
public double LengthSquared()
{
return (x * x + y * y + z * z);
}
/// <summary>
/// Clamp the values of the vector at the origin using the given tolerance
/// </summary>
/// <param name="tolerance">Tolerance</param>
public void ClampZero(double tolerance)
{
x = (tolerance > Math.Abs(x)) ? 0.0f : x;
y = (tolerance > Math.Abs(y)) ? 0.0f : y;
z = (tolerance > Math.Abs(z)) ? 0.0f : z;
}
/// <summary>
/// Clamp the values of the vector at the origin using the default tolerance
/// </summary>
/// <remarks>
/// Tolerant value is Double.Epsilon.
/// </remarks>
public void ClampZero()
{
ClampZero(Double.Epsilon);
}
/// <summary>
/// Calculating the scalar product of vectors
/// </summary>
/// <param name="vector">Second vector</param>
/// <returns>The scalar product</returns>
public double DotProduct(Vector3 vector)
{
return this.x * vector.X + this.y * vector.Y + this.z * vector.Z;
}
/// <summary>
/// Calculating the cross product of two vectors
/// </summary>
/// <param name="vector">Second vector</param>
/// <returns>The vector containing the result of the cross product</returns>
public Vector3 CrossProduct(Vector3 vector)
{
return new Vector3(
this.Y * vector.Z - this.Z * vector.Y,
this.Z * vector.X - this.X * vector.Z,
this.X * vector.Y - this.Y * vector.X);
}
/// <summary>
/// Computation of the angle between the vectors
/// </summary>
/// <param name="vector">Vector</param>
/// <returns>Angle in radians</returns>
public double Angle(Vector3 vector)
{
double cosResult = this.DotProduct(vector) / (this.Length() * vector.Length());
cosResult = (Math.Abs(cosResult) < 1.0) ? cosResult : (1.0 * Math.Sign(cosResult));
return Math.Acos(cosResult);
}
#endregion
#region Overrided methods
/// <summary>
/// Returns HASP key for this property
/// </summary>
/// <returns>HASP 32-bit key</returns>
public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
}
/// <summary>
/// Equivalence Checking with another object
/// </summary>
/// <param name="obj">The object for comparison</param>
/// <returns>True if an equivalent object, false otherwise</returns>
public override bool Equals(object obj)
{
if (obj is Vector3)
{
Vector3 v = (Vector3)obj;
return (x == v.X) && (y == v.Y) && (z == v.Z);
}
return false;
}
/// <summary>
/// Check with another vector equivalence
/// </summary>
/// <param name="vector">The vector for comparison</param>
/// <param name="tolerance">Tolerance</param>
/// <returns>True if an equivalent object, false otherwise</returns>
public bool Equals(Vector3 vector, double tolerance)
{
return ((Math.Abs(x - vector.X) < tolerance) && (Math.Abs(y - vector.Y) < tolerance) && (Math.Abs(z - vector.Z) < tolerance));
}
/// <summary>
/// The string representation of a object
/// </summary>
/// <returns>String - representation of the object</returns>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "({0}; {1}; {2})", x, y, z);
}
#endregion
#region Comparison operators
/// <summary>
/// Checks the equivalence of two vectors
/// </summary>
/// <param name="u">Left operand - vector</param>
/// <param name="v">The right operand - vector</param>
/// <returns>True if the two vectors are equal, false otherwise</returns>
public static bool operator ==(Vector3 u, Vector3 v)
{
return u.Equals((object)v);
}
/// <summary>
/// Checks for equality of two vectors
/// </summary>
/// <param name="u">The left operand - vector</param>
/// <param name="v">The right operand - vector</param>
/// <returns>True if the two vectors are different, false otherwise</returns>
public static bool operator !=(Vector3 u, Vector3 v)
{
return !u.Equals((object)v);
}
public static bool operator >(Vector3 u, Vector3 v)
{
return (
(u.x > v.x) &&
(u.y > v.y) &&
(u.z > v.z));
}
public static bool operator <(Vector3 u, Vector3 v)
{
return (
(u.x < v.x) &&
(u.y < v.y) &&
(u.z < v.z));
}
public static bool operator >=(Vector3 u, Vector3 v)
{
return (
(u.x >= v.x) &&
(u.y >= v.y) &&
(u.z >= v.z));
}
public static bool operator <=(Vector3 u, Vector3 v)
{
return (
(u.x <= v.x) &&
(u.y <= v.y) &&
(u.z <= v.z));
}
#endregion
#region Unary operators
/// <summary>
/// Inverting sign vector components
/// </summary>
/// <param name="v">Vector</param>
/// <returns>The new vector with inverted components</returns>
public static Vector3 operator -(Vector3 v)
{
return Vector3.Negate(v);
}
#endregion
#region Binary operators
public static Vector3 operator +(Vector3 u, Vector3 v)
{
return Vector3.Add(u, v);
}
public static Vector3 operator +(Vector3 v, double s)
{
return Vector3.Add(v, s);
}
public static Vector3 operator +(double s, Vector3 v)
{
return Vector3.Add(v, s);
}
public static Vector3 operator -(Vector3 u, Vector3 v)
{
return Vector3.Subtract(u, v);
}
public static Vector3 operator -(Vector3 v, double s)
{
return Vector3.Subtract(v, s);
}
public static Vector3 operator -(double s, Vector3 v)
{
return Vector3.Subtract(s, v);
}
public static Vector3 operator *(Vector3 v, double s)
{
return Vector3.Multiply(v, s);
}
public static Vector3 operator *(Vector3 v1, Vector3 v2)
{
return new Vector3(v1.X * v2.X, v1.Y * v2.Y, v1.Z * v2.Z);
}
public static Vector3 operator *(double s, Vector3 v)
{
return Vector3.Multiply(v, s);
}
public static Vector3 operator /(Vector3 v, double s)
{
return Vector3.Divide(v, s);
}
public static Vector3 operator /(double s, Vector3 v)
{
return Vector3.Divide(s, v);
}
#endregion
#region Operators access via indexes
/// <summary>
/// Get or set component by index ( [x, y] ).
/// </summary>
public double this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
#endregion
#region NvVector3
public static Vector3 Max(Vector3 a, Vector3 b)
{
return new Vector3(Math.Max(a.x, b.x), Math.Max(a.y, b.y), Math.Max(a.z, b.z));
}
public static Vector3 Min(Vector3 a, Vector3 b)
{
return new Vector3(Math.Min(a.x, b.x), Math.Min(a.y, b.y), Math.Min(a.z, b.z));
}
public static Vector3 Clamp(Vector3 v, float min, float max)
{
return new Vector3(NvMath.Clamp(v.x, min, max), NvMath.Clamp(v.y, min, max), NvMath.Clamp(v.z, min, max));
}
public static Vector3 Saturate(Vector3 v)
{
return new Vector3(NvMath.Saturate((float)v.x), NvMath.Saturate((float)v.y), NvMath.Saturate((float)v.z));
}
public static Vector3 Floor(Vector3 v)
{
return new Vector3(Math.Floor(v.x), Math.Floor(v.y), Math.Floor(v.z));
}
public static Vector3 Ceil(Vector3 v)
{
return new Vector3(Math.Ceiling(v.x), Math.Ceiling(v.y), Math.Ceiling(v.z));
}
public static Vector3 Lerp(Vector3 v1, Vector3 v2, float t)
{
float s = 1.0f - t;
return new Vector3(v1.x * s + t * v2.x, v1.y * s + t * v2.y, v1.z * s + t * v2.z);
}
#endregion
}
}