diff --git a/structs/Vector3d.cs b/structs/Vector3d.cs new file mode 100644 index 0000000..998bb25 --- /dev/null +++ b/structs/Vector3d.cs @@ -0,0 +1,264 @@ +using System.Diagnostics.CodeAnalysis; + +namespace GeometriCS.structs +{ + /// + /// Three-dimensional vector with double precision. + /// + public struct Vector3d + { + /// + /// Vector with 0 length. + /// + public static readonly Vector3d ZeroVector = new(0d, 0d, 0d); + + /// + /// Unit vector following the X axis, or (1; 0; 0) + /// + public static readonly Vector3d XAxis = new(1d, 0d, 0d); + + /// + /// Unit vector following the Y axis, or (0; 1; 0) + /// + public static readonly Vector3d YAxis = new(0d, 1d, 0d); + + /// + /// Unit vector following the Z axis, or (0; 0; 1) + /// + public static readonly Vector3d ZAxis = new(0d, 1d, 1d); + + /// + /// X component of the vector. + /// + public double X { get; set; } + + /// + /// Y component of the vector. + /// + public double Y { get; set; } + + /// + /// Z component of the vector. + /// + public double Z { get; set; } + + /// + /// Length of the vector. + /// + public double Length + { + get + { + return Math.Sqrt(X * X + Y * Y + Z * Z); + } + set + { + // If desired length is 0, set the components to 0 manually. + if (IsZero) + { + X = 0; + Y = 0; + Z = 0; + return; + } + + // New length divided by the old Length. + // Or, the scalar we need to apply to the vector in order for it to reach the desired length. + double scaleDelta = value / Length; + + // Scale the vector by the scalar delta. + X *= scaleDelta; + Y *= scaleDelta; + Z *= scaleDelta; + } + } + + /// + /// Is this a zero vecor? + /// + public bool IsZero => Utils.DoubleEquals(Length, 0); + + /// + /// Is this a unit vector? + /// + public bool IsUnit => Utils.DoubleEquals(Length, 1); + + /// + /// Normalized, or unit version of the vector. + /// + /// Calling this method does not affect the source vector. + /// Normalized vector, or (0; 0; 0) if original is (0; 0; 0). + public Vector3d Normalized() + { + // If this is a zero vector, return a zero vector. + if (IsZero) + { + return new Vector3d(0, 0, 0); + } + + // Get the inverse of the length of the vector, + // Or the scalar we need to apply to the vector in order for it to reach the desired length. + double scaleDelta = 1 / Length; + + // Return this vector scaled to have its Length equal to 1 + return new Vector3d(X * scaleDelta, Y * scaleDelta, Z * scaleDelta); + } + + /// + /// Compute the cross product between this and another vector. + /// + /// Other vector. + /// The cross product of the two vectors. + public Vector3d CrossProduct(Vector3d other) + { + return new Vector3d( + Y * other.Z - Z * other.Y, + Z * other.X - X * other.Z, + X * other.Y - Y * other.X); + } + + /// + /// Compute the dot product between this and another vector. + /// + /// Other vector. + /// The dot product of the two vectors. + public double DotProduct(Vector3d other) + { + return X * other.X + Y * other.Y + Z * other.Z; + } + + /// + /// Get the vector needed to get from the point at the end of this vector to the end of another vector. + /// + /// The vector to get to the end of. + /// Vector leading from the tip of this vector to the tip of the other vector. + public Vector3d GetVectorTo(Vector3d other) + { + return new Vector3d(-X + other.Y, -Y + other.Y, -Z + other.Z); + } + + /// + /// Constructor. + /// + /// X component of the vector. + /// X component of the vector. + /// Z component of the vector. + public Vector3d(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + /// + /// String representation of the vector. + /// + /// (X; Y; Z) + public override string ToString() + { + return $"({X}; {Y}; {Z})"; + } + + /// + /// Do the two objects represent the same vector? + /// + /// Object to compare. + /// true if the vectors are equal. Otherwise false + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is Vector3d asVect) + { + return this == asVect; + } + else + { + return false; + } + } + + public override int GetHashCode() + { + return HashCode.Combine(X, Y, Z); + } + + /// + /// Negation of the vector. + /// + /// Vector to negate. + /// Negated vector. + public static Vector3d operator -(Vector3d vect) + { + return new Vector3d(-vect.X, -vect.Y, -vect.Z); + } + + /// + /// Are the vectors equal? + /// + /// First vector. + /// Second vector. + /// true if the vectors are equal. Otherwise false + public static bool operator ==(Vector3d a, Vector3d b) + { + return Utils.DoubleEquals(a.X, b.X) && Utils.DoubleEquals(a.Y, b.Y) && Utils.DoubleEquals(a.Z, b.Z); + } + + /// + /// Are the vectors different? + /// + /// First vector. + /// Second vector. + /// true if the vectors are different. Otherwise false + public static bool operator !=(Vector3d a, Vector3d b) + { + return !(Utils.DoubleEquals(a.X, b.X) && Utils.DoubleEquals(a.Y, b.Y) && Utils.DoubleEquals(a.Z, b.Z)); + } + + /// + /// Add the vectors together. + /// + /// First vector. + /// Second vector. + /// a + b + public static Vector3d operator +(Vector3d a, Vector3d b) + { + return new Vector3d(a.X + b.X, a.Y + b.Y, a.Z + b.Z); + } + + /// + /// Add the first vector and the negation of the second negation. + /// + /// First vector. + /// Second vector. + /// a + (-b) + public static Vector3d operator -(Vector3d a, Vector3d b) + { + return new Vector3d(a.X - b.X, a.Y - b.Y, a.Z - b.Z); + } + + /// + /// Scale the vector by the scalar. + /// + /// Vector. + /// Scalar. + /// vect * scalar + public static Vector3d operator *(Vector3d vect, double scalar) + { + return new Vector3d(vect.X * scalar, vect.Y * scalar, vect.Z * scalar); + } + + /// + /// Scale the vector by the inverse of the scalar. + /// + /// Vector. + /// Scalar. + /// vect * (1 / scalar) + public static Vector3d operator /(Vector3d vect, double scalar) + { + if (Utils.DoubleEquals(scalar, 0)) + { + throw new DivideByZeroException("Cannot divide by zero, silly."); + } + return new Vector3d(vect.X / scalar, vect.Y / scalar, vect.Z / scalar); + } + } +}