From 6b2a9653ea688d82464198c59c44d066e6a5fc11 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Thu, 15 Oct 2020 14:57:41 +0100 Subject: [PATCH 1/3] Use C# 8.0 --- VpNet/VpNet.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/VpNet/VpNet.csproj b/VpNet/VpNet.csproj index 0ff6afa..5364761 100644 --- a/VpNet/VpNet.csproj +++ b/VpNet/VpNet.csproj @@ -16,6 +16,7 @@ https://github.com/VirtualParadise/VpNet http://edwin-share.virtualparadise.org/2017/05/vpsdkbutton.png + 8 From bee9ea49c8b8bb2afb14152a453e46d8ac191f9d Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Thu, 15 Oct 2020 14:58:29 +0100 Subject: [PATCH 2/3] Vector3 rewrite Adds helper methods and useful default values --- VpNet/Math/Vector3.cs | 681 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 645 insertions(+), 36 deletions(-) diff --git a/VpNet/Math/Vector3.cs b/VpNet/Math/Vector3.cs index 902f7f8..9413e87 100644 --- a/VpNet/Math/Vector3.cs +++ b/VpNet/Math/Vector3.cs @@ -1,36 +1,645 @@ -using System; -using System.Runtime.InteropServices; -using System.Xml.Serialization; - -namespace VpNet -{ - [Serializable] - [StructLayout(LayoutKind.Sequential)] - public struct Vector3 : IEquatable - { - [XmlAttribute] - public double X; - [XmlAttribute] - public double Y; - [XmlAttribute] - public double Z; - - public Vector3(double x, double y, double z) - { - X = x; - Y = y; - Z = z; - } - - public Vector3(double value) - { - X = Y = Z = value; - } - - public bool Equals(Vector3 other) => (X == other.X) && (Y == other.Y) && (Z == other.Z); - - public override bool Equals(object obj) => obj is Vector3 v && Equals(v); - - public override int GetHashCode() => X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode(); - } -} +namespace VpNet +{ + using System; + using System.Globalization; + using System.Runtime.CompilerServices; + using System.Text; + using System.Xml.Serialization; + + /// + /// Represents a vector with three double-precision floating-point values. + /// + [Serializable] + public struct Vector3 : IEquatable, IFormattable + { + /// + /// Represents a whose component values are <0, 0, 0>. + /// + public static readonly Vector3 Zero = new Vector3(0f); + + /// + /// Represents a whose component values are <1, 1, 1>. + /// + public static readonly Vector3 One = new Vector3(1f); + + /// + /// Represents a whose component values are <1, 0, 0>. + /// + public static readonly Vector3 UnitX = new Vector3(1f, 0f, 0f); + + /// + /// Represents a whose component values are <0, 1, 0>. + /// + public static readonly Vector3 UnitY = new Vector3(0f, 1f, 0f); + + /// + /// Represents a whose component values are <1, 0, 0>. + /// + public static readonly Vector3 UnitZ = new Vector3(0f, 0f, 1f); + + /// + /// Initializes a new instance of the struct by initializing all components to the same value. + /// + /// The value for each component. + public Vector3(double v) : this(v, v, v) + { + } + + /// + /// Initializes a new instance of the struct by copying the component values from another + /// instance of the struct. This is the copy constructor. + /// + /// The to copy. + public Vector3(Vector3 v) : this(v.X, v.Y, v.Z) + { + } + + /// + /// Initializes a new instance of the struct by initializing each component to specific values. + /// + /// The X component value. + /// The Y component value. + /// The Z component value. + public Vector3(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + /// + /// Gets the magnitude of this vector. + /// + public readonly double Magnitude + { + get => Distance(this, Zero); + } + + /// + /// Gets the square magnitude of this vector. + /// + public readonly double MagnitudeSquared + { + get => DistanceSquared(this, Zero); + } + + /// + /// Gets or sets the X component value. + /// + [XmlAttribute] + public double X { get; set; } + + /// + /// Gets or sets the Y component value. + /// + [XmlAttribute] + public double Y { get; set; } + + /// + /// Gets or sets the Z component value. + /// + [XmlAttribute] + public double Z { get; set; } + + /// + /// Adds two vectors together. + /// + /// The first source vector. + /// The second source vector. + /// The summed vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator +(Vector3 left, Vector3 right) + { + return new Vector3( + left.X + right.X, + left.Y + right.Y, + left.Z + right.Z + ); + } + + /// + /// Divides the first vector by the second. + /// + /// The first source vector. + /// The second source vector. + /// The vector resulting from the division. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator /(Vector3 left, Vector3 right) + { + return new Vector3( + left.X / right.X, + left.Y / right.Y, + left.Z / right.Z + ); + } + + /// + /// Divides the vector by the given divisor. + /// + /// The source vector. + /// The scalar value. + /// The vector resulting from the division. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator /(Vector3 dividend, double divisor) + { + return dividend / new Vector3(divisor); + } + + /// + /// Inversely divides the vector by the given divisor such that the result is divisor * (1 / dividend). + /// + /// The scalar value. + /// The source vector. + /// The vector resulting from the division. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator /(double dividend, Vector3 divisor) + { + return new Vector3(dividend) / divisor; + } + + /// + /// Returns a boolean indicating whether the two given vectors are equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// if the vectors are equal; otherwise. + public static bool operator ==(Vector3 left, Vector3 right) + { + return left.X.Equals(right.X) && + left.Y.Equals(right.Y) && + left.Z.Equals(right.Z); + } + + /// + /// Returns a boolean indicating whether the two given vectors are not equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// if the vectors are not equal; if they are equal. + public static bool operator !=(Vector3 left, Vector3 right) + { + return !(left == right); + } + + /// + /// Multiplies two vectors together. + /// + /// The first source vector. + /// The second source vector. + /// The product vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(Vector3 left, Vector3 right) + { + return new Vector3( + left.X * right.X, + left.Y * right.Y, + left.Z * right.Z + ); + } + + /// + /// Multiplies the vector by the given scalar. + /// + /// The source vector. + /// The scalar value. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(Vector3 vector, double scalar) + { + return vector * new Vector3(scalar); + } + + /// + /// Multiplies the vector by the given scalar. + /// + /// The scalar value. + /// The source vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(double scalar, Vector3 vector) + { + return new Vector3(scalar) * vector; + } + + /// + /// Subtracts the second vector from the first. + /// + /// The first source vector. + /// The second source vector. + /// The difference vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator -(Vector3 left, Vector3 right) + { + return new Vector3( + left.X - right.X, + left.Y - right.Y, + left.Z - right.Z + ); + } + + /// + /// Negates a given vector. + /// + /// The source vector. + /// The negated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator -(Vector3 value) + { + return Zero - value; + } + + /// + /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. + /// + /// A vector. + /// The absolute value vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Abs(Vector3 value) + { + var (x, y, z) = value; + return new Vector3( + Math.Abs(x), + Math.Abs(y), + Math.Abs(z) + ); + } + + /// + /// Adds two vectors together. + /// + /// The first source vector. + /// The second source vector. + /// The summed vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Add(Vector3 left, Vector3 right) + { + return left + right; + } + + /// + /// Restricts a vector between a min and max value. + /// + /// The source vector. + /// The minimum value. + /// The maximum value. + /// The restricted vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Clamp(Vector3 value, Vector3 min, Vector3 max) + { + return Min(Max(value, min), max); + } + + /// Computes the cross product of two vectors. + /// The first vector. + /// The second vector. + /// The cross product. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Cross(Vector3 value1, Vector3 value2) + { + return new Vector3( + (value1.Y * value2.Z) - (value1.Z * value2.Y), + (value1.Z * value2.X) - (value1.X * value2.Z), + (value1.X * value2.Y) - (value1.Y * value2.X) + ); + } + + /// + /// Returns the Euclidean distance between the two given points. + /// + /// The first vector. + /// The second vector. + /// The distance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Distance(Vector3 value1, Vector3 value2) + { + var distanceSquared = DistanceSquared(value1, value2); + return Math.Sqrt(distanceSquared); + } + + /// + /// Calculates the squared distance between two vectors. + /// + /// The first vector. + /// The second vector. + /// The square distance between and . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double DistanceSquared(Vector3 value1, Vector3 value2) + { + var difference = value1 - value2; + return Dot(difference, difference); + } + + /// + /// Divides the first vector by the second. + /// + /// The first source vector. + /// The second source vector. + /// The vector resulting from the division. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Divide(Vector3 dividend, Vector3 divisor) + { + return dividend / divisor; + } + + /// + /// Divides the vector by the given divisor. + /// + /// The source vector. + /// The scalar value. + /// The vector resulting from the division. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Divide(Vector3 dividend, double divisor) + { + return dividend / divisor; + } + + /// + /// Inversely divides the vector by the given divisor such that the result is divisor * (1 / dividend). + /// + /// The scalar value. + /// The source vector. + /// The vector resulting from the division. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Divide(double dividend, Vector3 divisor) + { + return dividend / divisor; + } + + /// + /// Returns the dot product of two vectors. + /// + /// The first vector. + /// The second vector. + /// The dot product. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Dot(Vector3 value1, Vector3 value2) + { + return (value1.X * value2.X) + + (value1.Y * value2.Y) + + (value1.Z * value2.Z); + } + + /// + /// Performs a linear interpolation between two vectors based on the given weighting. + /// + /// The first vector. + /// The second vector. + /// A value between 0 and 1 that indicates the weight of . + /// The interpolated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Lerp(Vector3 value1, Vector3 value2, double amount) + { + return (value1 * (1.0 - amount)) + (value2 * amount); + } + + /// + /// Returns a vector whose elements are the maximum of each of the pairs of elements in the two source vectors. + /// + /// The first source vector. + /// The second source vector. + /// The maximized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Max(Vector3 value1, Vector3 value2) + { + return new Vector3( + Math.Max(value1.X, value2.X), + Math.Max(value1.Y, value2.Y), + Math.Max(value1.Z, value2.Z) + ); + } + + /// + /// Returns a vector whose elements are the minimum of each of the pairs of elements in the two source vectors. + /// + /// The first source vector. + /// The second source vector. + /// The minimized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Min(Vector3 value1, Vector3 value2) + { + return new Vector3( + Math.Min(value1.X, value2.X), + Math.Min(value1.Y, value2.Y), + Math.Min(value1.Z, value2.Z) + ); + } + + /// + /// Multiplies two vectors together. + /// + /// The first source vector. + /// The second source vector. + /// The product vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Multiply(Vector3 left, Vector3 right) + { + return left * right; + } + + /// + /// Multiplies a vector by the given scalar. + /// + /// The source vector. + /// The scalar value. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Multiply(Vector3 vector, float scalar) + { + return vector * scalar; + } + + /// + /// Multiplies a vector by the given scalar. + /// + /// The scalar value. + /// The source vector. + /// The scaled vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Multiply(float scalar, Vector3 vector) + { + return scalar * vector; + } + + /// + /// Negates a given vector. + /// + /// The source vector. + /// The negated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Negate(Vector3 value) + { + return -value; + } + + /// + /// Returns a vector with the same direction as the given vector, but with magnitude 1. + /// + /// The vector to normalize. + /// The normalized vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Normalize(Vector3 value) + { + return value / value.Magnitude; + } + + /// + /// Returns the reflection of a vector off a surface that has the specified normal. + /// + /// The source vector. + /// The normal of the surface being reflected off. + /// The reflected vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Reflect(Vector3 vector, Vector3 normal) + { + var dot = Dot(vector, normal); + return vector - (2.0 * dot * normal); + } + + /// + /// Returns a vector whose elements are the square root of each of the source vector's elements. + /// + /// The source vector. + /// The square root vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Sqrt(Vector3 value) + { + return new Vector3( + Math.Sqrt(value.X), + Math.Sqrt(value.Y), + Math.Sqrt(value.Z) + ); + } + + /// + /// Subtracts the second vector from the first. + /// + /// The first source vector. + /// The second source vector. + /// The difference vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Subtract(Vector3 left, Vector3 right) + { + return left - right; + } + + /// + /// Copies the contents of the vector into the given array, starting from index. + /// + /// The destination array. + /// The index at which to copy the first element of the vector. + /// is . + /// is multidimensional. + /// + /// is greater than end of the array or index is less than zero. + /// + /// + /// The number of elements in source vector is greater than those available in destination array. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyTo(double[] array, int index = 0) + { + if (array is null) + { + throw new ArgumentNullException(nameof(array)); + } + + if ((index < 0) || (index >= array.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if ((array.Length - index) < 3) + { + throw new ArgumentException( + "The number of elements in source vector is greater than those available in destination array.", + nameof(array)); + } + + array[index] = X; + array[index + 1] = Y; + array[index + 2] = Z; + } + + /// + /// Deconstructs the current . + /// + /// The X component value of the current . + /// The Y component value of the current . + /// The Z component value of the current . + public readonly void Deconstruct(out double x, out double y, out double z) + { + x = X; + y = Y; + z = Z; + } + + /// + public readonly bool Equals(Vector3 other) + { + return this == other; + } + + /// + public override readonly bool Equals(object obj) + { + return obj is Vector3 other && Equals(other); + } + + /// + public override readonly int GetHashCode() + { + unchecked + { + var hashCode = X.GetHashCode(); + hashCode = (hashCode * 397) ^ Y.GetHashCode(); + hashCode = (hashCode * 397) ^ Z.GetHashCode(); + return hashCode; + } + } + + /// + /// Returns a representing this instance. + /// + /// The representation. + public override readonly string ToString() + { + return ToString("G", CultureInfo.CurrentCulture); + } + + /// + /// Returns a representing this instance, , using the specified format to + /// format individual elements. + /// + /// The format of individual elements. + /// The string representation. + public readonly string ToString(string? format) + { + return ToString(format, CultureInfo.CurrentCulture); + } + + /// + /// Returns a representing this instance, , using the specified format to + /// format individual elements. + /// + /// The format of individual elements. + /// The format provider to use when formatting elements. + /// The string representation. + public readonly string ToString(string? format, IFormatProvider? formatProvider) + { + var builder = new StringBuilder(); + var separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator; + + builder.Append('<') + .Append(X.ToString(format, formatProvider)) + .Append(separator) + .Append(' ') + .Append(Y.ToString(format, formatProvider)) + .Append(separator) + .Append(' ') + .Append(Z.ToString(format, formatProvider)) + .Append('>'); + + return builder.ToString(); + } + } +} From 93f286d04887dcdb1353ed1898d3559fffc5730f Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Thu, 15 Oct 2020 15:14:14 +0100 Subject: [PATCH 3/3] Sequential struct layout --- VpNet/Math/Vector3.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VpNet/Math/Vector3.cs b/VpNet/Math/Vector3.cs index 9413e87..9153c57 100644 --- a/VpNet/Math/Vector3.cs +++ b/VpNet/Math/Vector3.cs @@ -3,6 +3,7 @@ namespace VpNet using System; using System.Globalization; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; using System.Text; using System.Xml.Serialization; @@ -10,6 +11,7 @@ namespace VpNet /// Represents a vector with three double-precision floating-point values. /// [Serializable] + [StructLayout(LayoutKind.Sequential)] public struct Vector3 : IEquatable, IFormattable { ///