using System.Diagnostics.CodeAnalysis; using Speckle.Newtonsoft.Json; using Speckle.Objects.Other; using Speckle.Sdk.Common; using Speckle.Sdk.Models; namespace Speckle.Objects.Geometry; /// /// A 3-dimensional vector /// [SpeckleType("Objects.Geometry.Vector")] public class Vector : Base, IHasBoundingBox, ITransformable { /// public Vector() { } /// /// Constructs a new 2D from it's X and Y coordinates. /// /// The x coordinate of the vector /// The y coordinate of the vector /// The units the coordinates are in. /// The unique application ID of the object. [SetsRequiredMembers] public Vector(double x, double y, double z, string units, string? applicationId = null) { this.x = x; this.y = y; this.z = z; this.applicationId = applicationId; this.units = units; } /// /// The unit's this is in. /// This should be one of /// public required string units { get; set; } /// /// The x coordinate of the vector. /// public required double x { get; set; } /// /// The y coordinate of the vector. /// public required double y { get; set; } /// /// The z coordinate of the vector. /// public required double z { get; set; } /// /// Gets the Euclidean length of this vector. /// /// Length of the vector. [JsonIgnore] public double Length => Math.Sqrt(DotProduct(this, this)); /// public Box? bbox { get; set; } /// public bool TransformTo(Transform transform, out Vector transformed) { var m = transform.matrix; var tX = x * m.M11 + y * m.M12 + z * m.M13; var tY = x * m.M21 + y * m.M22 + z * m.M23; var tZ = x * m.M31 + y * m.M32 + z * m.M33; transformed = new Vector(tX, tY, tZ, units, applicationId); return true; } /// public bool TransformTo(Transform transform, out ITransformable transformed) { _ = TransformTo(transform, out Vector vec); transformed = vec; return true; } /// /// Returns the coordinates of this as a list of numbers /// /// A list of coordinates {x, y, z} public List ToList() => [x, y, z]; public Point ToPoint() => new(x, y, z, units, applicationId); /// /// Creates a new vector based on a list of coordinates and the unit they're drawn in. /// /// The list of coordinates {x, y, z} /// The units the coordinates are in /// A new with the provided coordinates. public static Vector FromList(IReadOnlyList list, string units) { return new Vector(list[0], list[1], list[2], units); } /// /// Divides a vector by a numerical value. This will divide each coordinate by the provided value. /// /// The vector to divide /// The value to divide by /// The resulting public static Vector operator /(Vector vector, double val) => new(vector.x / val, vector.y / val, vector.z / val, vector.units); /// /// Multiplies a vector by a numerical value. This will multiply each coordinate by the provided value. /// /// The vector to multiply /// The value to multiply by /// The resulting public static Vector operator *(Vector vector, double val) => new(vector.x * val, vector.y * val, vector.z * val, vector.units); /// /// Adds two vectors by adding each of their coordinates. /// /// The first vector /// The second vector /// The resulting public static Vector operator +(Vector vector1, Vector vector2) => new(vector1.x + vector2.x, vector1.y + vector2.y, vector1.z + vector2.z, vector1.units); /// /// Subtracts two vectors by subtracting each of their coordinates. /// /// The first vector /// The second vector /// The resulting public static Vector operator -(Vector vector1, Vector vector2) => new(vector1.x - vector2.x, vector1.y - vector2.y, vector1.z - vector2.z, vector1.units); /// /// Gets the scalar product (dot product) of two given vectors /// Dot product = u1*v1 + u2*v2 + u3*v3. /// /// First vector. /// Second vector. /// Numerical value of the dot product. public static double DotProduct(Vector u, Vector v) { return u.x * v.x + u.y * v.y + u.z * v.z; } /// /// Computes the vector product (cross product) of two given vectors /// Cross product = { u2 * v3 - u3 * v2; u3 * v1 - u1 * v3; u1 * v2 - u2 * v1 }. /// /// First vector. /// Second vector. /// Vector result of the cross product. public static Vector CrossProduct(Vector u, Vector v) { if (u.units != v.units && u.units != Units.None && v.units != Units.None) { throw new ArgumentException("Cannot perform cross product on two vectors with different unit systems"); } var x = u.y * v.z - u.z * v.y; var y = u.z * v.x - u.x * v.z; var z = u.x * v.y - u.y * v.x; return new Vector(x, y, z, units: u.units); } /// /// Compute and return a unit vector from this vector /// /// a normalized unit vector public void Normalize() { var length = Length; x /= length; y /= length; z /= length; } /// /// Inverses the direction of the vector, equivalent to multiplying by -1 /// /// A pointing in the opposite direction public Vector Negate() { x *= -1; y *= -1; z *= -1; return this; } /// /// Gets or sets the coordinates of the vector /// [ JsonProperty(NullValueHandling = NullValueHandling.Ignore), Obsolete("Use X,Y,Z fields to access coordinates instead", true) ] public List value { #pragma warning disable CS8603 // Possible null reference return. get => null; #pragma warning restore CS8603 // Possible null reference return. set { x = value[0]; y = value[1]; z = value.Count > 2 ? value[2] : 0; } } }