/* * Copyright (c) 2006-2014, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Runtime.InteropServices; using System.Globalization; namespace OpenMetaverse { [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Vector4 : IComparable, IEquatable { /// X value public float X; /// Y value public float Y; /// Z value public float Z; /// W value public float W; #region Constructors public Vector4(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; } public Vector4(Vector2 value, float z, float w) { X = value.X; Y = value.Y; Z = z; W = w; } public Vector4(Vector3 value, float w) { X = value.X; Y = value.Y; Z = value.Z; W = w; } public Vector4(float value) { X = value; Y = value; Z = value; W = value; } /// /// Constructor, builds a vector from a byte array /// /// Byte array containing four four-byte floats /// Beginning position in the byte array public Vector4(byte[] byteArray, int pos) { X = Y = Z = W = 0f; FromBytes(byteArray, pos); } public Vector4(Vector4 value) { X = value.X; Y = value.Y; Z = value.Z; W = value.W; } #endregion Constructors #region Public Methods public float Length() { return (float)Math.Sqrt(DistanceSquared(this, Zero)); } public float LengthSquared() { return DistanceSquared(this, Zero); } public void Normalize() { this = Normalize(this); } /// /// Test if this vector is equal to another vector, within a given /// tolerance range /// /// Vector to test against /// The acceptable magnitude of difference /// between the two vectors /// True if the magnitude of difference between the two vectors /// is less than the given tolerance, otherwise false public bool ApproxEquals(Vector4 vec, float tolerance) { Vector4 diff = this - vec; return (diff.LengthSquared() <= tolerance * tolerance); } /// /// IComparable.CompareTo implementation /// public int CompareTo(Vector4 vector) { return Length().CompareTo(vector.Length()); } /// /// Test if this vector is composed of all finite numbers /// public bool IsFinite() { return (Utils.IsFinite(X) && Utils.IsFinite(Y) && Utils.IsFinite(Z) && Utils.IsFinite(W)); } /// /// Builds a vector from a byte array /// /// Byte array containing a 16 byte vector /// Beginning position in the byte array public void FromBytes(byte[] byteArray, int pos) { if (!BitConverter.IsLittleEndian) { // Big endian architecture byte[] conversionBuffer = new byte[16]; Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 16); Array.Reverse(conversionBuffer, 0, 4); Array.Reverse(conversionBuffer, 4, 4); Array.Reverse(conversionBuffer, 8, 4); Array.Reverse(conversionBuffer, 12, 4); X = BitConverter.ToSingle(conversionBuffer, 0); Y = BitConverter.ToSingle(conversionBuffer, 4); Z = BitConverter.ToSingle(conversionBuffer, 8); W = BitConverter.ToSingle(conversionBuffer, 12); } else { // Little endian architecture X = BitConverter.ToSingle(byteArray, pos); Y = BitConverter.ToSingle(byteArray, pos + 4); Z = BitConverter.ToSingle(byteArray, pos + 8); W = BitConverter.ToSingle(byteArray, pos + 12); } } /// /// Returns the raw bytes for this vector /// /// A 16 byte array containing X, Y, Z, and W public byte[] GetBytes() { byte[] byteArray = new byte[16]; ToBytes(byteArray, 0); return byteArray; } /// /// Writes the raw bytes for this vector to a byte array /// /// Destination byte array /// Position in the destination array to start /// writing. Must be at least 16 bytes before the end of the array public void ToBytes(byte[] dest, int pos) { Buffer.BlockCopy(BitConverter.GetBytes(X), 0, dest, pos + 0, 4); Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, dest, pos + 4, 4); Buffer.BlockCopy(BitConverter.GetBytes(Z), 0, dest, pos + 8, 4); Buffer.BlockCopy(BitConverter.GetBytes(W), 0, dest, pos + 12, 4); if (!BitConverter.IsLittleEndian) { Array.Reverse(dest, pos + 0, 4); Array.Reverse(dest, pos + 4, 4); Array.Reverse(dest, pos + 8, 4); Array.Reverse(dest, pos + 12, 4); } } #endregion Public Methods #region Static Methods public static Vector4 Add(Vector4 value1, Vector4 value2) { value1.W += value2.W; value1.X += value2.X; value1.Y += value2.Y; value1.Z += value2.Z; return value1; } public static Vector4 Clamp(Vector4 value1, Vector4 min, Vector4 max) { return new Vector4( Utils.Clamp(value1.X, min.X, max.X), Utils.Clamp(value1.Y, min.Y, max.Y), Utils.Clamp(value1.Z, min.Z, max.Z), Utils.Clamp(value1.W, min.W, max.W)); } public static float Distance(Vector4 value1, Vector4 value2) { return (float)Math.Sqrt(DistanceSquared(value1, value2)); } public static float DistanceSquared(Vector4 value1, Vector4 value2) { return (value1.W - value2.W) * (value1.W - value2.W) + (value1.X - value2.X) * (value1.X - value2.X) + (value1.Y - value2.Y) * (value1.Y - value2.Y) + (value1.Z - value2.Z) * (value1.Z - value2.Z); } public static Vector4 Divide(Vector4 value1, Vector4 value2) { value1.W /= value2.W; value1.X /= value2.X; value1.Y /= value2.Y; value1.Z /= value2.Z; return value1; } public static Vector4 Divide(Vector4 value1, float divider) { float factor = 1f / divider; value1.W *= factor; value1.X *= factor; value1.Y *= factor; value1.Z *= factor; return value1; } public static float Dot(Vector4 vector1, Vector4 vector2) { return vector1.X * vector2.X + vector1.Y * vector2.Y + vector1.Z * vector2.Z + vector1.W * vector2.W; } public static Vector4 Lerp(Vector4 value1, Vector4 value2, float amount) { return new Vector4( Utils.Lerp(value1.X, value2.X, amount), Utils.Lerp(value1.Y, value2.Y, amount), Utils.Lerp(value1.Z, value2.Z, amount), Utils.Lerp(value1.W, value2.W, amount)); } public static Vector4 Max(Vector4 value1, Vector4 value2) { return new Vector4( Math.Max(value1.X, value2.X), Math.Max(value1.Y, value2.Y), Math.Max(value1.Z, value2.Z), Math.Max(value1.W, value2.W)); } public static Vector4 Min(Vector4 value1, Vector4 value2) { return new Vector4( Math.Min(value1.X, value2.X), Math.Min(value1.Y, value2.Y), Math.Min(value1.Z, value2.Z), Math.Min(value1.W, value2.W)); } public static Vector4 Multiply(Vector4 value1, Vector4 value2) { value1.W *= value2.W; value1.X *= value2.X; value1.Y *= value2.Y; value1.Z *= value2.Z; return value1; } public static Vector4 Multiply(Vector4 value1, float scaleFactor) { value1.W *= scaleFactor; value1.X *= scaleFactor; value1.Y *= scaleFactor; value1.Z *= scaleFactor; return value1; } public static Vector4 Negate(Vector4 value) { value.X = -value.X; value.Y = -value.Y; value.Z = -value.Z; value.W = -value.W; return value; } public static Vector4 Normalize(Vector4 vector) { const float MAG_THRESHOLD = 0.0000001f; float factor = DistanceSquared(vector, Zero); if (factor > MAG_THRESHOLD) { factor = 1f / (float)Math.Sqrt(factor); vector.X *= factor; vector.Y *= factor; vector.Z *= factor; vector.W *= factor; } else { vector.X = 0f; vector.Y = 0f; vector.Z = 0f; vector.W = 0f; } return vector; } public static Vector4 SmoothStep(Vector4 value1, Vector4 value2, float amount) { return new Vector4( Utils.SmoothStep(value1.X, value2.X, amount), Utils.SmoothStep(value1.Y, value2.Y, amount), Utils.SmoothStep(value1.Z, value2.Z, amount), Utils.SmoothStep(value1.W, value2.W, amount)); } public static Vector4 Subtract(Vector4 value1, Vector4 value2) { value1.W -= value2.W; value1.X -= value2.X; value1.Y -= value2.Y; value1.Z -= value2.Z; return value1; } public static Vector4 Transform(Vector2 position, Matrix4 matrix) { return new Vector4( (position.X * matrix.M11) + (position.Y * matrix.M21) + matrix.M41, (position.X * matrix.M12) + (position.Y * matrix.M22) + matrix.M42, (position.X * matrix.M13) + (position.Y * matrix.M23) + matrix.M43, (position.X * matrix.M14) + (position.Y * matrix.M24) + matrix.M44); } public static Vector4 Transform(Vector3 position, Matrix4 matrix) { return new Vector4( (position.X * matrix.M11) + (position.Y * matrix.M21) + (position.Z * matrix.M31) + matrix.M41, (position.X * matrix.M12) + (position.Y * matrix.M22) + (position.Z * matrix.M32) + matrix.M42, (position.X * matrix.M13) + (position.Y * matrix.M23) + (position.Z * matrix.M33) + matrix.M43, (position.X * matrix.M14) + (position.Y * matrix.M24) + (position.Z * matrix.M34) + matrix.M44); } public static Vector4 Transform(Vector4 vector, Matrix4 matrix) { return new Vector4( (vector.X * matrix.M11) + (vector.Y * matrix.M21) + (vector.Z * matrix.M31) + (vector.W * matrix.M41), (vector.X * matrix.M12) + (vector.Y * matrix.M22) + (vector.Z * matrix.M32) + (vector.W * matrix.M42), (vector.X * matrix.M13) + (vector.Y * matrix.M23) + (vector.Z * matrix.M33) + (vector.W * matrix.M43), (vector.X * matrix.M14) + (vector.Y * matrix.M24) + (vector.Z * matrix.M34) + (vector.W * matrix.M44)); } public static Vector4 Parse(string val) { char[] splitChar = { ',' }; string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar); return new Vector4( float.Parse(split[0].Trim(), Utils.EnUsCulture), float.Parse(split[1].Trim(), Utils.EnUsCulture), float.Parse(split[2].Trim(), Utils.EnUsCulture), float.Parse(split[3].Trim(), Utils.EnUsCulture)); } public static bool TryParse(string val, out Vector4 result) { try { result = Parse(val); return true; } catch (Exception) { result = new Vector4(); return false; } } #endregion Static Methods #region Overrides public override bool Equals(object obj) { return (obj is Vector4) ? this == (Vector4)obj : false; } public bool Equals(Vector4 other) { return W == other.W && X == other.X && Y == other.Y && Z == other.Z; } public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode(); } public override string ToString() { return String.Format(Utils.EnUsCulture, "<{0}, {1}, {2}, {3}>", X, Y, Z, W); } /// /// Get a string representation of the vector elements with up to three /// decimal digits and separated by spaces only /// /// Raw string representation of the vector public string ToRawString() { CultureInfo enUs = new CultureInfo("en-us"); enUs.NumberFormat.NumberDecimalDigits = 3; return String.Format(enUs, "{0} {1} {2} {3}", X, Y, Z, W); } #endregion Overrides #region Operators public static bool operator ==(Vector4 value1, Vector4 value2) { return value1.W == value2.W && value1.X == value2.X && value1.Y == value2.Y && value1.Z == value2.Z; } public static bool operator !=(Vector4 value1, Vector4 value2) { return !(value1 == value2); } public static Vector4 operator +(Vector4 value1, Vector4 value2) { value1.W += value2.W; value1.X += value2.X; value1.Y += value2.Y; value1.Z += value2.Z; return value1; } public static Vector4 operator -(Vector4 value) { return new Vector4(-value.X, -value.Y, -value.Z, -value.W); } public static Vector4 operator -(Vector4 value1, Vector4 value2) { value1.W -= value2.W; value1.X -= value2.X; value1.Y -= value2.Y; value1.Z -= value2.Z; return value1; } public static Vector4 operator *(Vector4 value1, Vector4 value2) { value1.W *= value2.W; value1.X *= value2.X; value1.Y *= value2.Y; value1.Z *= value2.Z; return value1; } public static Vector4 operator *(Vector4 value1, float scaleFactor) { value1.W *= scaleFactor; value1.X *= scaleFactor; value1.Y *= scaleFactor; value1.Z *= scaleFactor; return value1; } public static Vector4 operator /(Vector4 value1, Vector4 value2) { value1.W /= value2.W; value1.X /= value2.X; value1.Y /= value2.Y; value1.Z /= value2.Z; return value1; } public static Vector4 operator /(Vector4 value1, float divider) { float factor = 1f / divider; value1.W *= factor; value1.X *= factor; value1.Y *= factor; value1.Z *= factor; return value1; } #endregion Operators /// A vector with a value of 0,0,0,0 public readonly static Vector4 Zero = new Vector4(); /// A vector with a value of 1,1,1,1 public readonly static Vector4 One = new Vector4(1f, 1f, 1f, 1f); /// A vector with a value of 1,0,0,0 public readonly static Vector4 UnitX = new Vector4(1f, 0f, 0f, 0f); /// A vector with a value of 0,1,0,0 public readonly static Vector4 UnitY = new Vector4(0f, 1f, 0f, 0f); /// A vector with a value of 0,0,1,0 public readonly static Vector4 UnitZ = new Vector4(0f, 0f, 1f, 0f); /// A vector with a value of 0,0,0,1 public readonly static Vector4 UnitW = new Vector4(0f, 0f, 0f, 1f); } }