using System; using UnityEngine; using System.Collections.Generic; using System.Text; using URandom = UnityEngine.Random; using MRandom = Unity.Mathematics.Random; namespace ProceduralToolkit { /// /// Class for generating random data. Contains extensions for arrays and other collections. /// public static class RandomE { /// /// Returns a random 2D rotation /// public static float rotation2 => URandom.Range(0, 360f); /// /// Returns a random rotation around X axis /// public static Quaternion xRotation => Quaternion.Euler(rotation2, 0, 0); /// /// Returns a random rotation around Y axis /// public static Quaternion yRotation => Quaternion.Euler(0, rotation2, 0); /// /// Returns a random rotation around Z axis /// public static Quaternion zRotation => Quaternion.Euler(0, 0, rotation2); #region Geometry /// /// Returns a random point on a circle with radius 1 /// public static Vector2 onUnitCircle2 => Geometry.PointOnCircle2(1, rotation2); /// /// Returns a random point inside a circle with radius 1 /// public static Vector3 insideUnitCircle3XY => Geometry.PointOnCircle3XY(URandom.value, rotation2); /// /// Returns a random point inside a circle with radius 1 /// public static Vector3 insideUnitCircle3XZ => Geometry.PointOnCircle3XZ(URandom.value, rotation2); /// /// Returns a random point inside a circle with radius 1 /// public static Vector3 insideUnitCircle3YZ => Geometry.PointOnCircle3YZ(URandom.value, rotation2); /// /// Returns a random point on a circle with radius 1 /// public static Vector3 onUnitCircle3XY => Geometry.PointOnCircle3XY(1, rotation2); /// /// Returns a random point on a circle with radius 1 /// public static Vector3 onUnitCircle3XZ => Geometry.PointOnCircle3XZ(1, rotation2); /// /// Returns a random point on a circle with radius 1 /// public static Vector3 onUnitCircle3YZ => Geometry.PointOnCircle3YZ(1, rotation2); /// /// Returns a random point inside a unit square /// public static Vector2 insideUnitSquare => Range(new Vector2(-0.5f, -0.5f), new Vector2(0.5f, 0.5f)); /// /// Returns a random point on the perimeter of a unit square /// public static Vector2 onUnitSquare => PointOnRect(new Rect(-0.5f, -0.5f, 1, 1)); /// /// Returns a random point inside a unit cube /// public static Vector3 insideUnitCube => Range(new Vector3(-0.5f, -0.5f, -0.5f), new Vector3(0.5f, 0.5f, 0.5f)); /// /// Returns a random point on a segment /// public static Vector2 PointOnSegment2(Segment2 segment) { return PointOnSegment2(segment.a, segment.b); } /// /// Returns a random point on a segment /// public static Vector2 PointOnSegment2(Vector2 segmentA, Vector2 segmentB) { return Geometry.PointOnSegment2(segmentA, segmentB, URandom.value); } /// /// Returns a random point on a segment /// public static Vector3 PointOnSegment3(Segment3 segment) { return PointOnSegment3(segment.a, segment.b); } /// /// Returns a random point on a segment /// public static Vector3 PointOnSegment3(Vector3 segmentA, Vector3 segmentB) { return Geometry.PointOnSegment3(segmentA, segmentB, URandom.value); } /// /// Returns a random point on a circle /// public static Vector2 PointOnCircle2(Circle2 circle) { return PointOnCircle2(circle.center, circle.radius); } /// /// Returns a random point on a circle /// public static Vector2 PointOnCircle2(Vector2 center, float radius) { return Geometry.PointOnCircle2(center, radius, rotation2); } /// /// Returns a random point inside a circle /// public static Vector2 PointInCircle2(Circle2 circle) { return PointInCircle2(circle.center, circle.radius); } /// /// Returns a random point inside a circle /// public static Vector2 PointInCircle2(Vector2 center, float radius) { return center + URandom.insideUnitCircle*radius; } /// /// Returns a random point on a sphere /// public static Vector3 PointOnSphere(Sphere sphere) { return PointOnSphere(sphere.center, sphere.radius); } /// /// Returns a random point on a sphere /// public static Vector3 PointOnSphere(Vector3 center, float radius) { return center + URandom.onUnitSphere*radius; } /// /// Returns a random point inside a sphere /// public static Vector3 PointInSphere(Sphere sphere) { return PointInSphere(sphere.center, sphere.radius); } /// /// Returns a random point inside a sphere /// public static Vector3 PointInSphere(Vector3 center, float radius) { return center + URandom.insideUnitSphere*radius; } /// /// Returns a random point inside a /// public static Vector2 PointInRect(Rect rect) { return Range(rect.min, rect.max); } /// /// Returns a random point on the perimeter of a /// public static Vector2 PointOnRect(Rect rect) { float perimeter = 2*rect.width + 2*rect.height; float value = URandom.value*perimeter; if (value < rect.width) { return rect.min + new Vector2(value, 0); } value -= rect.width; if (value < rect.height) { return rect.min + new Vector2(rect.width, value); } value -= rect.height; if (value < rect.width) { return rect.min + new Vector2(value, rect.height); } return rect.min + new Vector2(0, value - rect.width); } /// /// Returns a random point inside /// public static Vector3 PointInBounds(Bounds bounds) { return Range(bounds.min, bounds.max); } #endregion Geometry #region Colors /// /// Returns a random color between black [inclusive] and white [inclusive] /// public static Color color => new Color(URandom.value, URandom.value, URandom.value); /// /// Returns a color with a random hue and a maximum saturation and value in HSV model /// public static ColorHSV colorHSV => new ColorHSV(URandom.value, 1, 1); /// /// Returns a gradient between two random colors /// public static Gradient gradient => ColorE.Gradient(color, color); /// /// Returns a gradient between two random HSV colors /// public static Gradient gradientHSV => ColorE.Gradient(colorHSV, colorHSV); /// /// Returns a color with a random hue and a given and /// public static ColorHSV ColorHue(float saturation, float value, float alpha = 1) { return new ColorHSV(URandom.value, saturation, value, alpha); } /// /// Returns a color with a random saturation and given and /// public static ColorHSV ColorSaturation(float hue, float value, float alpha = 1) { return new ColorHSV(hue, URandom.value, value, alpha); } /// /// Returns a color with a random value and given and /// public static ColorHSV ColorValue(float hue, float saturation, float alpha = 1) { return new ColorHSV(hue, saturation, URandom.value, alpha); } /// /// Returns an analogous palette based on a color with a random hue /// public static List AnalogousPalette(float saturation = 1, float value = 1, float alpha = 1, int count = 2, bool withComplementary = false) { return ColorHue(saturation, value, alpha).GetAnalogousPalette(count, withComplementary); } /// /// Returns a triadic palette based on a color with a random hue /// public static List TriadicPalette(float saturation = 1, float value = 1, float alpha = 1, bool withComplementary = false) { return ColorHue(saturation, value, alpha).GetTriadicPalette(withComplementary); } /// /// Returns a tetradic palette based on a color with a random hue /// public static List TetradicPalette(float saturation = 1, float value = 1, float alpha = 1) { return ColorHue(saturation, value, alpha).GetTetradicPalette(); } #endregion Colors #region Strings /// /// Returns a random alphanumeric 8-character string /// public static string string8 => PTUtils.Alphanumerics.GetRandom(8); /// /// Returns a random alphanumeric 16-character string /// public static string string16 => PTUtils.Alphanumerics.GetRandom(16); /// /// Returns a random lowercase letter /// public static char lowercaseLetter => PTUtils.LowercaseLetters.GetRandom(); /// /// Returns a random uppercase letter /// public static char uppercaseLetter => PTUtils.UppercaseLetters.GetRandom(); #endregion Strings /// /// Returns a random element /// public static T GetRandom(this IReadOnlyList list) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (list.Count == 0) { throw new ArgumentException("Empty list"); } return list[URandom.Range(0, list.Count)]; } /// /// Returns a random element /// public static T GetRandom(this IReadOnlyList list, ref MRandom random) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (list.Count == 0) { throw new ArgumentException("Empty list"); } return list[random.NextInt(0, list.Count)]; } /// /// Returns a random element /// public static T GetRandom(T item1, T item2, params T[] items) { if (items == null) { throw new ArgumentNullException(nameof(items)); } int index = URandom.Range(0, items.Length + 2); if (index == 0) { return item1; } if (index == 1) { return item2; } return items[index - 2]; } /// /// Returns a random element with the chances of rolling based on /// /// Positive floats representing weights. Negative values may lead to unpredictable behaviour. public static T GetRandom(this IReadOnlyList list, IReadOnlyList weights) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (list.Count == 0) { throw new ArgumentException("Empty list"); } if (weights == null) { throw new ArgumentNullException(nameof(weights)); } if (weights.Count == 0) { throw new ArgumentException("Empty weights"); } if (list.Count != weights.Count) { throw new ArgumentException("Array sizes must be equal"); } if (list.Count == 1) { return list[0]; } var cumulative = new List(weights); for (int i = 1; i < cumulative.Count; i++) { cumulative[i] += cumulative[i - 1]; } float randomValue = URandom.Range(0, cumulative[cumulative.Count - 1]); int index = cumulative.FindIndex(a => a >= randomValue); if (index == -1) { throw new ArgumentException("Weights must be positive"); } return list[index]; } /// /// Returns a random element with the chances of rolling based on /// /// Positive floats representing weights. Negative values may lead to unpredictable behaviour. public static T GetRandom(this IReadOnlyList list, IReadOnlyList weights, ref MRandom random) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (list.Count == 0) { throw new ArgumentException("Empty list"); } if (weights == null) { throw new ArgumentNullException(nameof(weights)); } if (weights.Count == 0) { throw new ArgumentException("Empty weights"); } if (list.Count != weights.Count) { throw new ArgumentException("Array sizes must be equal"); } if (list.Count == 1) { return list[0]; } var cumulative = new List(weights); for (int i = 1; i < cumulative.Count; i++) { cumulative[i] += cumulative[i - 1]; } float randomValue = random.NextFloat(0, cumulative[cumulative.Count - 1]); int index = cumulative.FindIndex(a => a >= randomValue); if (index == -1) { throw new ArgumentException("Weights must be positive"); } return list[index]; } /// /// Returns a random value from the dictionary /// public static TValue GetRandom(this IDictionary dictionary) { if (dictionary == null) { throw new ArgumentNullException(nameof(dictionary)); } var keys = dictionary.Keys; if (keys.Count == 0) { throw new ArgumentException("Empty dictionary"); } return dictionary[new List(keys).GetRandom()]; } /// /// Returns a random value from the dictionary /// public static TValue GetRandom(this IDictionary dictionary, ref MRandom random) { if (dictionary == null) { throw new ArgumentNullException(nameof(dictionary)); } var keys = dictionary.Keys; if (keys.Count == 0) { throw new ArgumentException("Empty dictionary"); } return dictionary[new List(keys).GetRandom(ref random)]; } /// /// Returns a random character from the string /// public static char GetRandom(this string chars) { if (string.IsNullOrEmpty(chars)) { throw new ArgumentException("Empty string"); } return chars[URandom.Range(0, chars.Length)]; } /// /// Returns a string consisting of random characters from that string /// public static string GetRandom(this string chars, int length) { if (string.IsNullOrEmpty(chars)) { throw new ArgumentException("Empty string"); } var randomString = new StringBuilder(length); for (int i = 0; i < length; i++) { randomString.Append(chars[URandom.Range(0, chars.Length)]); } return randomString.ToString(); } /// /// Returns a random element and removes it from the list /// public static T PopRandom(this List list) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (list.Count == 0) { throw new ArgumentException("Empty list"); } int index = URandom.Range(0, list.Count); T item = list[index]; list.RemoveAt(index); return item; } /// /// Returns a random element and removes it from the list /// public static T PopRandom(this List list, ref MRandom random) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (list.Count == 0) { throw new ArgumentException("Empty list"); } int index = random.NextInt(0, list.Count); T item = list[index]; list.RemoveAt(index); return item; } /// /// Fisher–Yates shuffle /// /// /// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle /// public static void Shuffle(this IList list) { if (list == null) { throw new ArgumentNullException(nameof(list)); } for (int i = 0; i < list.Count; i++) { int j = URandom.Range(i, list.Count); T tmp = list[j]; list[j] = list[i]; list[i] = tmp; } } /// /// Fisher–Yates shuffle /// /// /// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle /// public static void Shuffle(this IList list, ref MRandom random) { if (list == null) { throw new ArgumentNullException(nameof(list)); } for (int i = 0; i < list.Count; i++) { int j = random.NextInt(i, list.Count); T tmp = list[j]; list[j] = list[i]; list[i] = tmp; } } /// /// Returns true with probability /// /// between 0.0 [inclusive] and 1.0 [inclusive] public static bool Chance(float percent) { if (percent == 0) return false; if (percent == 1) return true; return URandom.value < percent; } #region Range /// /// Returns a random vector between [inclusive] and [inclusive] /// public static Vector2 Range(Vector2 min, Vector2 max) { return new Vector2(URandom.Range(min.x, max.x), URandom.Range(min.y, max.y)); } /// /// Returns a random vector between [inclusive] and [inclusive] /// public static Vector3 Range(Vector3 min, Vector3 max) { return new Vector3(URandom.Range(min.x, max.x), URandom.Range(min.y, max.y), URandom.Range(min.z, max.z)); } /// /// Returns a random vector between [inclusive] and [inclusive] /// public static Vector4 Range(Vector4 min, Vector4 max) { return new Vector4(URandom.Range(min.x, max.x), URandom.Range(min.y, max.y), URandom.Range(min.z, max.z), URandom.Range(min.w, max.w)); } /// /// Returns a random vector between [inclusive] and [exclusive] /// public static Vector2Int Range(Vector2Int min, Vector2Int max) { return new Vector2Int(URandom.Range(min.x, max.x), URandom.Range(min.y, max.y)); } /// /// Returns a random vector between [inclusive] and [exclusive] /// public static Vector3Int Range(Vector3Int min, Vector3Int max) { return new Vector3Int(URandom.Range(min.x, max.x), URandom.Range(min.y, max.y), URandom.Range(min.z, max.z)); } /// /// Returns a random float number between and [inclusive] and [inclusive]. /// Ensures that there will be only specified amount of variants. /// public static float Range(float min, float max, int variants) { if (variants < 2) { throw new ArgumentException("Variants must be greater than one"); } return Mathf.Lerp(min, max, URandom.Range(0, variants)/(variants - 1f)); } /// /// Returns a random vector between and [inclusive] and [inclusive]. /// Ensures that there will be only specified amount of variants. /// public static Vector2 Range(Vector2 min, Vector2 max, int variants) { return new Vector2(Range(min.x, max.x, variants), Range(min.y, max.y, variants)); } /// /// Returns a random vector between and [inclusive] and [inclusive]. /// Ensures that there will be only specified amount of variants. /// public static Vector3 Range(Vector3 min, Vector3 max, int variants) { return new Vector3(Range(min.x, max.x, variants), Range(min.y, max.y, variants), Range(min.z, max.z, variants)); } /// /// Returns a random vector between and [inclusive] and [inclusive]. /// Ensures that there will be only specified amount of variants. /// public static Vector4 Range(Vector4 min, Vector4 max, int variants) { return new Vector4(Range(min.x, max.x, variants), Range(min.y, max.y, variants), Range(min.z, max.z, variants), Range(min.w, max.w, variants)); } #endregion Range } }