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