// 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); } } /// /// 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 . /// A thats either true or false. public static bool NextBool(this Random random) => random.Next(2) == 1; /// /// Generates a random with a weight value to adjust preference. /// /// 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) => weight >= NextFloat(random); /// /// Generates a random value between min and max. /// /// An instance of . /// The lower boundary. /// The upper boundary. /// A between min and max. public static byte NextByte(this Random random, byte min = 0, byte max = 2) { return (byte)random.Next(min, max); } /// /// Generates a random value between min and max. /// /// An instance of . /// The min value. /// The max value. /// A random between min and max. public static float NextFloat(this Random random, float min = 0.0f, float max = 2.0f) { return (float)random.NextDouble() * (max - min) + min; } /// /// 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, 180), NextFloat(random, -180, 180), randomRoll ? NextFloat(random, -180, 180) : 0); } /// /// Generates a uniformly distributed random unit length vector point on a unit sphere. /// /// An instance of . /// A random . public static Vector3 NextVector3(this Random random) { Vector3 output; 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; } while (output.LengthSquared > 1 || output.LengthSquared < 1e-6f); output *= (1.0f / (float)Math.Sqrt(output.LengthSquared)); return output; } /// /// Generates a uniformly distributed random unit length vector point on a unit sphere in 2D. /// /// An instance of . /// A random . public static Vector2 NextVector2(this Random random) { Vector2 output; do { output.X = NextFloat(random) * 2.0f - 1.0f; output.Y = NextFloat(random) * 2.0f - 1.0f; } while (output.LengthSquared > 1 || output.LengthSquared < 1e-6f); output *= (1.0f / (float)Math.Sqrt(output.LengthSquared)); return output; } /// /// Generates a random . /// /// An instance of . /// Should the color be generated from a single random Hue value or separate values for each channel. /// Randomize the alpha value. /// A nice random . public static Color NextColor(this Random random, bool trueRandom, bool randomAlpha) { float alpha = randomAlpha ? NextFloat(random) : 1f; if (!trueRandom) return Color.FromHSV(NextFloat(random, 0f, 360f), 1f, 1f, alpha); return new Color( NextFloat(random, 0f, 255f), NextFloat(random, 0f, 255f), NextFloat(random, 0f, 255f), alpha); } /// /// Gets a random . /// /// An instance of . /// The maximum value /// A random public static double NextDouble(this Random random, double maxValue = 2.0d) { return random.NextDouble() * maxValue; } /// /// Gets a random . /// /// An instance of . /// The minimum value /// The maximum value /// A random public static double NextDouble(this Random random, double minValue = 0.0d, double maxValue = 2.0d) { return random.NextDouble() * (maxValue - minValue) + minValue; } /// /// 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) & 9223372036854775807L); } /// /// Generates a random normalized 2D direction . /// /// An instance of . /// A random normalized 2D direction . public static Vector2 NextDirection2D(this Random random) { return Vector2.Normalize(new Vector2((float)random.NextDouble(), (float)random.NextDouble())); } /// /// Generates a random normalized 3D direction . /// /// An instance of . /// A random normalized 3D direction . public static Vector3 NextDirection3D(this Random random) { return Vector3.Normalize(new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); } /// /// Generates a random point in a circle of a given radius. /// /// An instance of . /// Radius of circle. Default 1.0f. /// A random point in a circle of a given radius. public static Vector2 PointInACircle(this Random random, float radius = 1.0f) { var randomRadius = (float)random.NextDouble() * radius; return new Vector2 { X = (float)Math.Cos(random.NextDouble()) * randomRadius, Y = (float)Math.Sin(random.NextDouble()) * randomRadius, }; } } }