namespace GeometriCS { /// /// Three-dimensional vector with double precision. /// public class Vector3d : ICloneable { /// /// 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(object? obj) { if (obj is Vector3d asVect) { return this == asVect; } else { return false; } } public override int GetHashCode() { return HashCode.Combine(X, Y, Z); } /// /// Deep copy the vector. /// /// Deep copy of the vector. public object Clone() { return new Vector3d(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); } } }