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;
}
}
}