// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace FlaxEngine.Utilities { /// /// Collection of various extension methods. /// public static partial class Extensions { /// /// Creates deep clone for a class if all members of this class are marked as serializable (uses Json serialization). /// /// The input instance of an object. /// The instance type of an object. /// Returns new object of provided class. public static T DeepClone(this T instance) where T : new() { var json = Json.JsonSerializer.Serialize(instance); return Json.JsonSerializer.Deserialize(json); } /// /// Creates raw clone for a structure using memory copy. Valid only for value types. /// /// The input instance of an object. /// The instance type of an object. /// Returns new object of provided structure. public static T RawClone(this T instance) { IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(instance)); try { Marshal.StructureToPtr(instance, ptr, false); return (T)Marshal.PtrToStructure(ptr, instance.GetType()); } finally { Marshal.FreeHGlobal(ptr); } } /// /// Checks if the text is multiline. /// /// Text to check. /// True if text is a multiline, otherwise false. public static bool IsMultiline(this string str) { for (int i = 0; i < str.Length; i++) { if (str[i] == '\n') return true; } return false; } /// /// Splits string into lines /// /// Text to split /// True if remove empty lines, otherwise keep them /// Array with all lines public static string[] GetLines(this string str, bool removeEmptyLines = false) { return str.Split(new[] { "\r\n", "\r", "\n" }, removeEmptyLines ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None); } /// /// Adds the elements of the specified collection to the end of the . /// /// The type of elements in the collection. /// The to add items to. /// The collection whose elements should be added to the end of the . It can contain elements that are , if type is a reference type. /// If or are . public static void AddRange(this ICollection destination, IEnumerable collection) { if (destination == null) throw new ArgumentNullException(nameof(destination)); if (collection == null) throw new ArgumentNullException(nameof(collection)); foreach (var item in collection) { destination.Add(item); } } /// /// Enqueues the elements of the specified collection to the . /// /// The type of elements in the collection. /// The to add items to. /// The collection whose elements should be added to the . It can contain elements that are , if type is a reference type. /// If or are . public static void EnqueueRange(this Queue queue, IEnumerable collection) { if (queue == null) throw new ArgumentNullException(nameof(queue)); if (collection == null) throw new ArgumentNullException(nameof(collection)); foreach (var item in collection) { queue.Enqueue(item); } } /// /// Pushes the elements of the specified collection to the . /// /// The type of elements in the collection. /// The to add items to. /// The collection whose elements should be pushed on to the . It can contain elements that are , if type is a reference type. /// If or are . public static void PushRange(this Stack stack, IEnumerable collection) { if (stack == null) throw new ArgumentNullException(nameof(stack)); if (collection == null) throw new ArgumentNullException(nameof(collection)); foreach (var item in collection) { stack.Push(item); } } /// /// Performs the specified action on each element of the . /// /// The type of the elements of the input sequence. /// The sequence of elements to execute the . /// The delegate to perform on each element of the 1. /// or is . public static void ForEach(this IEnumerable source, Action action) { if (source == null) throw new ArgumentNullException(nameof(source)); if (action == null) throw new ArgumentNullException(nameof(action)); foreach (var item in source) { action(item); } } /// /// Chooses a random item from the collection. /// /// The type of the elements of the input sequence. /// An instance of . /// Collection to choose item from. /// A random item from collection /// If the random argument is null. /// If the collection is null. public static T Choose(this Random random, IList collection) { if (random == null) throw new ArgumentNullException(nameof(random)); if (collection == null) throw new ArgumentNullException(nameof(collection)); return collection[random.Next(collection.Count)]; } /// /// Chooses a random item. /// /// The type of the elements of the input sequence. /// An instance of . /// Collection to choose item from. /// A random item from collection /// If the random is null. /// If the collection is null. public static T Choose(this Random random, params T[] collection) { if (random == null) throw new ArgumentNullException(nameof(random)); if (collection == null) throw new ArgumentNullException(nameof(collection)); return collection[random.Next(collection.Length)]; } /// /// Shuffles the collection in place. /// /// The type of the elements of the input sequence. /// An instance of . /// Collection to shuffle. /// If the random argument is null. /// If the random collection is null. public static void Shuffle(this Random random, IList collection) { if (random == null) { throw new ArgumentNullException(nameof(random)); } if (collection == null) { throw new ArgumentNullException(nameof(collection)); } int n = collection.Count; while (n > 1) { n--; int k = random.Next(n + 1); T value = collection[k]; collection[k] = collection[n]; collection[n] = value; } } /// /// Generates a random . /// /// An instance of . /// Normalized value that determines the chance to return true. /// A thats either true or false. public static bool NextBool(this Random random, float weight = 0.5f) => random.NextDouble() < weight; /// /// Generates a random value up until an exclusive maximum. /// /// An instance of . /// The maximum value. If it's zero, a maximum of 256 is used /// A random between min and max. public static byte NextByte(this Random random, byte max = 0) { return max == 0 ? (byte)(random.Next() % 256) : (byte)random.Next(max); } /// /// Generates a random value between min and max. /// /// An instance of . /// The minimum value. /// The maximum value. /// A random between min and max. public static byte NextByte(this Random random, byte min, byte max) { return (byte)random.Next(min, max); } /// /// Generates a random value between min and max. /// /// An instance of . /// The minimum value. /// The maximum value. /// A random between min and max. public static float NextFloat(this Random random, float min = 0.0f, float max = 1.0f) { return (float)random.NextDouble() * (max - min) + min; } /// /// Generates a random value between 0 and max. /// /// An instance of . /// The maximum value. /// A random between min and max. public static float NextFloat(this Random random, float max) { return (float)random.NextDouble() * max; } /// /// Generates a random . /// /// An instance of . /// Should the roll value be randomized. /// A random . public static Quaternion NextQuaternion(this Random random, bool randomRoll = false) { return Quaternion.Euler(NextFloat(random, -180.0f, 180.0f), NextFloat(random, -180.0f, 180.0f), randomRoll ? NextFloat(random, -180.0f, 180.0f) : 0.0f); } /// /// Generates a random point in a circle of a given radius. /// /// An instance of . /// Radius of circle. Default 1.0f./>. /// A random . public static Vector2 NextUnitVector2(this Random random, float radius = 1.0f) { var randomRadius = (float)random.NextDouble() * radius; return new Vector2((float)Math.Cos(random.NextDouble()) * randomRadius, (float)Math.Sin(random.NextDouble()) * randomRadius); } /// /// Generates a uniformly distributed random unit length vector point on a unit sphere. /// /// An instance of . /// A random . public static Vector3 NextUnitVector3(this Random random) { Vector3 output; float l; do { // Create random float with a mean of 0 and deviation of ±1 output.X = NextFloat(random) * 2.0f - 1.0f; output.Y = NextFloat(random) * 2.0f - 1.0f; output.Z = NextFloat(random) * 2.0f - 1.0f; l = output.LengthSquared; } while (l > 1 || l < Mathf.Epsilon); output.Normalize(); return output; } /// /// Gets a random with components in a given range. /// /// An instance of . /// The minimum value. /// The maximum value. /// A random . public static Vector2 NextVector2(this Random random, float min = 0.0f, float max = 1.0f) { return new Vector2(NextFloat(random, min, max), NextFloat(random, min, max)); } /// /// Gets a random with components in a given range. /// /// An instance of . /// The minimum value. /// The maximum value. /// A random . public static Vector3 NextVector3(this Random random, float min = 0.0f, float max = 1.0f) { return new Vector3(NextFloat(random, min, max), NextFloat(random, min, max), NextFloat(random, min, max)); } /// /// Gets a random with components in a given range. /// /// An instance of . /// The minimum value. /// The maximum value. /// A random . public static Vector4 NextVector4(this Random random, float min = 0.0f, float max = 1.0f) { return new Vector4(NextFloat(random, min, max), NextFloat(random, min, max), NextFloat(random, min, max), NextFloat(random, min, max)); } /// /// Generates a random . /// /// An instance of . /// Randomize the alpha value. /// A nice random . public static Color NextColor(this Random random, bool randomAlpha = false) { return new Color(NextFloat(random), NextFloat(random), NextFloat(random), randomAlpha ? NextFloat(random) : 1.0f); } /// /// Generates a random . /// /// An instance of . /// Randomize the alpha value. /// A nice random . public static ColorHSV NextColorHSV(this Random random, bool randomAlpha = false) { return new ColorHSV(NextFloat(random, 0.0f, 360.0f), 1.0f, 1.0f, randomAlpha ? NextFloat(random) : 1.0f); } /// /// Gets a random . /// /// An instance of . /// The minimum value. /// The maximum value. /// A random . public static double NextDouble(this Random random, double min = 0.0d, double max = 1.0d) { return random.NextDouble() * (max - min) + min; } /// /// Gets a random . /// /// An instance of . /// The maximum value. /// A random . public static double NextDouble(this Random random, double max = 1.0d) { return random.NextDouble() * max; } /// /// Gets a random . /// /// An instance of . /// A random . internal static long NextLong(this Random random) { var numArray = new byte[8]; random.NextBytes(numArray); return (long)(BitConverter.ToUInt64(numArray, 0) & long.MaxValue); } /// /// Returns a random value of the given enum. /// /// The enum to get the value from. /// An instance of . /// A random enum value. public static TEnum NextEnum(this Random random) { Array values = Enum.GetValues(typeof(TEnum)); return (TEnum)values.GetValue(random.Next(values.Length)); } } }