409 lines
16 KiB
C#
409 lines
16 KiB
C#
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace FlaxEngine.Utilities
|
|
{
|
|
/// <summary>
|
|
/// Collection of various extension methods.
|
|
/// </summary>
|
|
public static partial class Extensions
|
|
{
|
|
/// <summary>
|
|
/// Creates deep clone for a class if all members of this class are marked as serializable (uses Json serialization).
|
|
/// </summary>
|
|
/// <param name="instance">The input instance of an object.</param>
|
|
/// <typeparam name="T">The instance type of an object.</typeparam>
|
|
/// <returns>Returns new object of provided class.</returns>
|
|
public static T DeepClone<T>(this T instance)
|
|
where T : new()
|
|
{
|
|
var json = Json.JsonSerializer.Serialize(instance);
|
|
return Json.JsonSerializer.Deserialize<T>(json);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates raw clone for a structure using memory copy. Valid only for value types.
|
|
/// </summary>
|
|
/// <param name="instance">The input instance of an object.</param>
|
|
/// <typeparam name="T">The instance type of an object.</typeparam>
|
|
/// <returns>Returns new object of provided structure.</returns>
|
|
public static T RawClone<T>(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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Splits string into lines
|
|
/// </summary>
|
|
/// <param name="str">Text to split</param>
|
|
/// <param name="removeEmptyLines">True if remove empty lines, otherwise keep them</param>
|
|
/// <returns>Array with all lines</returns>
|
|
public static string[] GetLines(this string str, bool removeEmptyLines = false)
|
|
{
|
|
return str.Split(new[]
|
|
{
|
|
"\r\n",
|
|
"\r",
|
|
"\n"
|
|
}, removeEmptyLines ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random double.
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <param name="maxValue">The maximum value</param>
|
|
/// <returns>A random double</returns>
|
|
public static double NextDouble(this Random random, double maxValue)
|
|
{
|
|
return random.NextDouble() * maxValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random double.
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <param name="minValue">The minimum value</param>
|
|
/// <param name="maxValue">The maximum value</param>
|
|
/// <returns>A random double</returns>
|
|
public static double NextDouble(this Random random, double minValue, double maxValue)
|
|
{
|
|
return random.NextDouble() * (maxValue - minValue) + minValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random float.
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <returns>A random float</returns>
|
|
public static float NextFloat(this Random random)
|
|
{
|
|
return (float)random.NextDouble();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random float.
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <param name="maxValue">The maximum value</param>
|
|
/// <returns>A random float</returns>
|
|
public static float NextFloat(this Random random, float maxValue)
|
|
{
|
|
return (float)random.NextDouble() * maxValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random float.
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <param name="minValue">The minimum value</param>
|
|
/// <param name="maxValue">The maximum value</param>
|
|
/// <returns></returns>
|
|
public static float NextFloat(this Random random, float minValue, float maxValue)
|
|
{
|
|
return (float)random.NextDouble() * (maxValue - minValue) + minValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random Color.
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <returns></returns>
|
|
public static Color NextColor(this Random random)
|
|
{
|
|
return new Color(NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random Vector2 with components in range [0;1].
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <returns></returns>
|
|
public static Vector2 NextVector2(this Random random)
|
|
{
|
|
return new Vector2(NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random Vector3 with components in range [0;1].
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <returns></returns>
|
|
public static Vector3 NextVector3(this Random random)
|
|
{
|
|
return new Vector3(NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random Vector4 with components in range [0;1].
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <returns></returns>
|
|
public static Vector4 NextVector4(this Random random)
|
|
{
|
|
return new Vector4(NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f),
|
|
NextFloat(random, 1.0f));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random Quaternion.
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <returns></returns>
|
|
public static Quaternion NextQuaternion(this Random random)
|
|
{
|
|
return Quaternion.Euler(NextFloat(random, -180, 180),
|
|
NextFloat(random, -180, 180),
|
|
NextFloat(random, -180, 180));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a random 64-bit signed integer value.
|
|
/// </summary>
|
|
/// <param name="random">The random.</param>
|
|
/// <returns></returns>
|
|
internal static long NextLong(this Random random)
|
|
{
|
|
var numArray = new byte[8];
|
|
random.NextBytes(numArray);
|
|
return (long)(BitConverter.ToUInt64(numArray, 0) & 9223372036854775807L);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a random normalized 2D direction vector.
|
|
/// </summary>
|
|
/// <param name="random">An instance of <see cref="Random"/>.</param>
|
|
/// <returns>A random normalized 2D direction vector.</returns>
|
|
public static Vector2 NextDirection2D(this Random random)
|
|
{
|
|
return Vector2.Normalize(new Vector2((float)random.NextDouble(), (float)random.NextDouble()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a random normalized 3D direction vector.
|
|
/// </summary>
|
|
/// <param name="random">An instance of <see cref="Random"/>.</param>
|
|
/// <returns>A random normalized 3D direction vector.</returns>
|
|
public static Vector3 NextDirection3D(this Random random)
|
|
{
|
|
return Vector3.Normalize(new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a random point in a circle of a given radius.
|
|
/// </summary>
|
|
/// <param name="random">An instance of <see cref="Random"/>.</param>
|
|
/// <param name="radius">Radius of circle. Default 1.0f.</param>
|
|
/// <returns>A random point in a circle of a given radius.</returns>
|
|
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,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the elements of the specified collection to the end of the <see cref="ICollection{T}"/>.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
|
/// <param name="destination">The <see cref="ICollection{T}"/> to add items to.</param>
|
|
/// <param name="collection">The collection whose elements should be added to the end of the <paramref name="destination"/>. It can contain elements that are <see langword="null"/>, if type <typeparamref name="T"/> is a reference type.</param>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="destination"/> or <paramref name="collection"/> are <see langword="null"/>.</exception>
|
|
public static void AddRange<T>(this ICollection<T> destination, IEnumerable<T> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enqueues the elements of the specified collection to the <see cref="Queue{T}"/>.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
|
/// <param name="queue">The <see cref="Queue{T}"/> to add items to.</param>
|
|
/// <param name="collection">The collection whose elements should be added to the <paramref name="queue"/>. It can contain elements that are <see langword="null"/>, if type <typeparamref name="T"/> is a reference type.</param>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="queue"/> or <paramref name="collection"/> are <see langword="null"/>.</exception>
|
|
public static void EnqueueRange<T>(this Queue<T> queue, IEnumerable<T> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pushes the elements of the specified collection to the <see cref="Stack{T}"/>.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
|
/// <param name="stack">The <see cref="Stack{T}"/> to add items to.</param>
|
|
/// <param name="collection">The collection whose elements should be pushed on to the <paramref name="stack"/>. It can contain elements that are <see langword="null"/>, if type <typeparamref name="T"/> is a reference type.</param>
|
|
/// <exception cref="ArgumentNullException">If <paramref name="stack"/> or <paramref name="collection"/> are <see langword="null"/>.</exception>
|
|
public static void PushRange<T>(this Stack<T> stack, IEnumerable<T> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs the specified action on each element of the <see cref="IEnumerable{T}"/>.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements of the input sequence.</typeparam>
|
|
/// <param name="source">The sequence of elements to execute the <see cref="IEnumerable{T}"/>.</param>
|
|
/// <param name="action">The <see cref="Action{T}"/> delegate to perform on each element of the <see cref="IEnumerable{T}"/>1.</param>
|
|
/// <exception cref="ArgumentException"><paramref name="source"/> or <paramref name="action"/> is <see langword="null"/>.</exception>
|
|
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
|
|
{
|
|
if (source == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(source));
|
|
}
|
|
|
|
if (action == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(action));
|
|
}
|
|
|
|
foreach (var item in source)
|
|
{
|
|
action(item);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chooses a random item from the collection.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements of the input sequence.</typeparam>
|
|
/// <param name="random">An instance of <see cref="Random"/>.</param>
|
|
/// <param name="collection">Collection to choose item from.</param>
|
|
/// <returns>A random item from collection</returns>
|
|
/// <exception cref="ArgumentNullException">If the random argument is null.</exception>
|
|
/// <exception cref="ArgumentNullException">If the collection is null.</exception>
|
|
public static T Choose<T>(this Random random, IList<T> collection)
|
|
{
|
|
if (random == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(random));
|
|
}
|
|
|
|
if (collection == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(collection));
|
|
}
|
|
|
|
return collection[random.Next(collection.Count)];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chooses a random item.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements of the input sequence.</typeparam>
|
|
/// <param name="random">An instance of <see cref="Random"/>.</param>
|
|
/// <param name="collection">Collection to choose item from.</param>
|
|
/// <returns>A random item from collection</returns>
|
|
/// <exception cref="ArgumentNullException">If the random is null.</exception>
|
|
/// <exception cref="ArgumentNullException">If the collection is null.</exception>
|
|
public static T Choose<T>(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)];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shuffles the collection in place.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements of the input sequence.</typeparam>
|
|
/// <param name="random">An instance of <see cref="Random"/>.</param>
|
|
/// <param name="collection">Collection to shuffle.</param>
|
|
/// <exception cref="ArgumentNullException">If the random argument is null.</exception>
|
|
/// <exception cref="ArgumentNullException">If the random collection is null.</exception>
|
|
public static void Shuffle<T>(this Random random, IList<T> 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;
|
|
}
|
|
}
|
|
}
|
|
}
|