1 using System;
2 using UnityEngine;
3 using System.Collections.Generic;
4 using System.Text;
5 using Random = UnityEngine.Random;
6
7 namespace ProceduralToolkit
8 {
9 /// <summary>
10 /// Class for generating random data. Contains extensions for arrays and other classes.
11 /// </summary>
12 public static class RandomE
13 {
14 #region Vectors
15
16 /// <summary>
17 /// Returns a random point on a circle with radius 1
18 /// </summary>
19 public static Vector2 onUnitCircle2 { get { return PTUtils.PointOnCircle2(1, Random.Range(0, 360f)); } }
20
21 /// <summary>
22 /// Returns a random point on a circle with radius 1
23 /// </summary>
24 public static Vector2 onUnitCircle3XY { get { return PTUtils.PointOnCircle3XY(1, Random.Range(0, 360f)); } }
25
26 /// <summary>
27 /// Returns a random point on a circle with radius 1
28 /// </summary>
29 public static Vector2 onUnitCircle3XZ { get { return PTUtils.PointOnCircle3XZ(1, Random.Range(0, 360f)); } }
30
31 /// <summary>
32 /// Returns a random point on a circle with radius 1
33 /// </summary>
34 public static Vector2 onUnitCircle3YZ { get { return PTUtils.PointOnCircle3YZ(1, Random.Range(0, 360f)); } }
35
36 /// <summary>
37 /// Returns a random point inside a unit square
38 /// </summary>
39 public static Vector2 insideUnitSquare { get { return new Vector2(Random.value, Random.value); } }
40
41 /// <summary>
42 /// Returns a random point on the perimeter of a unit square
43 /// </summary>
44 public static Vector2 onUnitSquare { get { return PointOnSquare(1, 1); } }
45
46 /// <summary>
47 /// Returns a random point inside a unit cube
48 /// </summary>
49 public static Vector3 insideUnitCube { get { return new Vector3(Random.value, Random.value, Random.value); } }
50
51 #endregion Vectors
52
53 #region Colors
54
55 /// <summary>
56 /// Returns a random color between black [inclusive] and white [inclusive]
57 /// </summary>
58 public static Color color { get { return new Color(Random.value, Random.value, Random.value); } }
59
60 /// <summary>
61 /// Returns a color with random hue and maximum saturation and value in HSV model
62 /// </summary>
63 public static ColorHSV colorHSV { get { return new ColorHSV(Random.value, 1, 1); } }
64
65 /// <summary>
66 /// Returns a gradient between two random colors
67 /// </summary>
68 public static Gradient gradient { get { return ColorE.Gradient(color, color); } }
69
70 /// <summary>
71 /// Returns a gradient between two random HSV colors
72 /// </summary>
73 public static Gradient gradientHSV { get { return ColorE.Gradient(colorHSV, colorHSV); } }
74
75 /// <summary>
76 /// Returns a color with random hue and given <paramref name="saturation"/> and <paramref name="value"/>
77 /// </summary>
78 public static ColorHSV ColorHue(float saturation, float value, float alpha = 1)
79 {
80 return new ColorHSV(Random.value, saturation, value, alpha);
81 }
82
83 /// <summary>
84 /// Returns a color with random saturation and given <paramref name="hue"/> and <paramref name="value"/>
85 /// </summary>
86 public static ColorHSV ColorSaturation(float hue, float value, float alpha = 1)
87 {
88 return new ColorHSV(hue, Random.value, value, alpha);
89 }
90
91 /// <summary>
92 /// Returns a color with random value and given <paramref name="hue"/> and <paramref name="saturation"/>
93 /// </summary>
94 public static ColorHSV ColorValue(float hue, float saturation, float alpha = 1)
95 {
96 return new ColorHSV(hue, saturation, Random.value, alpha);
97 }
98
99 /// <summary>
100 /// Returns a analogous palette based on a color with random hue
101 /// </summary>
102 public static List<ColorHSV> AnalogousPalette(
103 float saturation = 1,
104 float value = 1,
105 float alpha = 1,
106 int count = 2,
107 bool withComplementary = false)
108 {
109 return ColorHue(saturation, value, alpha).GetAnalogousPalette(count, withComplementary);
110 }
111
112 /// <summary>
113 /// Returns a triadic palette based on a color with random hue
114 /// </summary>
115 public static List<ColorHSV> TriadicPalette(
116 float saturation = 1,
117 float value = 1,
118 float alpha = 1,
119 bool withComplementary = false)
120 {
121 return ColorHue(saturation, value, alpha).GetTriadicPalette(withComplementary);
122 }
123
124 /// <summary>
125 /// Returns a tetradic palette based on a color with random hue
126 /// </summary>
127 public static List<ColorHSV> TetradicPalette(float saturation = 1, float value = 1, float alpha = 1)
128 {
129 return ColorHue(saturation, value, alpha).GetTetradicPalette();
130 }
131
132 #endregion Colors
133
134 #region Strings
135
136 /// <summary>
137 /// Returns a random alphanumeric 8-character string
138 /// </summary>
139 public static string string8 { get { return PTUtils.alphanumerics.GetRandom(8); } }
140
141 /// <summary>
142 /// Returns a random alphanumeric 16-character string
143 /// </summary>
144 public static string string16 { get { return PTUtils.alphanumerics.GetRandom(16); } }
145
146 /// <summary>
147 /// Returns a random lowercase letter
148 /// </summary>
149 public static char lowercaseLetter { get { return PTUtils.lowercase.GetRandom(); } }
150
151 /// <summary>
152 /// Returns a random uppercase letter
153 /// </summary>
154 public static char uppercaseLetter { get { return PTUtils.uppercase.GetRandom(); } }
155
156 #endregion Strings
157
158 /// <summary>
159 /// Returns a random element
160 /// </summary>
161 public static T GetRandom<T>(this List<T> items)
162 {
163 if (items == null)
164 {
165 throw new ArgumentNullException("items");
166 }
167 if (items.Count == 0)
168 {
169 Debug.LogError("Empty array");
170 return default(T);
171 }
172 return items[Random.Range(0, items.Count)];
173 }
174
175 /// <summary>
176 /// Returns a random element
177 /// </summary>
178 public static T GetRandom<T>(this T[] items)
179 {
180 if (items == null)
181 {
182 throw new ArgumentNullException("items");
183 }
184 if (items.Length == 0)
185 {
186 Debug.LogError("Empty array");
187 return default(T);
188 }
189 return items[Random.Range(0, items.Length)];
190 }
191
192 /// <summary>
193 /// Returns a random element
194 /// </summary>
195 public static T GetRandom<T>(T item1, T item2, params T[] items)
196 {
197 int index = Random.Range(0, items.Length + 2);
198 if (index == 0)
199 {
200 return item1;
201 }
202 if (index == 1)
203 {
204 return item2;
205 }
206 return items[index - 2];
207 }
208
209 /// <summary>
210 /// Returns a random value from dictionary
211 /// </summary>
212 public static TValue GetRandom<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
213 {
214 if (dictionary == null)
215 {
216 throw new ArgumentNullException("dictionary");
217 }
218 var keys = dictionary.Keys;
219 if (keys.Count == 0)
220 {
221 Debug.LogError("Empty dictionary");
222 return default(TValue);
223 }
224 return dictionary[new List<TKey>(keys).GetRandom()];
225 }
226
227 /// <summary>
228 /// Returns a random element with chances for roll of each element based on <paramref name="weights"/>
229 /// </summary>
230 /// <param name="weights">Positive floats representing chances</param>
231 public static T GetRandom<T>(this List<T> list, List<float> weights)
232 {
233 if (list == null)
234 {
235 throw new ArgumentNullException("list");
236 }
237 if (list.Count == 0)
238 {
239 Debug.LogError("Empty array");
240 return default(T);
241 }
242 if (weights == null)
243 {
244 throw new ArgumentNullException("weights");
245 }
246 if (weights.Count == 0)
247 {
248 Debug.LogError("Empty weights");
249 return default(T);
250 }
251 if (list.Count != weights.Count)
252 {
253 Debug.LogError("Array sizes must be equal");
254 return list.GetRandom();
255 }
256
257 if (list.Count == 1)
258 {
259 return list[0];
260 }
261
262 var cumulative = new List<float>(weights);
263 for (int i = 1; i < cumulative.Count; i++)
264 {
265 cumulative[i] += cumulative[i - 1];
266 }
267
268 float random = Random.Range(0, cumulative[cumulative.Count - 1]);
269 int index = cumulative.FindIndex(a => a >= random);
270 if (index == -1)
271 {
272 Debug.LogError("Invalid weights");
273 return list.GetRandom();
274 }
275 return list[index];
276 }
277
278 /// <summary>
279 /// Returns a random character from string
280 /// </summary>
281 public static char GetRandom(this string chars)
282 {
283 if (string.IsNullOrEmpty(chars))
284 {
285 Debug.LogError("Empty string");
286 return default(char);
287 }
288 return chars[Random.Range(0, chars.Length)];
289 }
290
291 /// <summary>
292 /// Returns a random string consisting of characters from that string
293 /// </summary>
294 public static string GetRandom(this string chars, int length)
295 {
296 if (string.IsNullOrEmpty(chars))
297 {
298 Debug.LogError("Empty string");
299 return default(string);
300 }
301 var randomString = new StringBuilder(length);
302 for (int i = 0; i < length; i++)
303 {
304 randomString.Append(chars[Random.Range(0, chars.Length)]);
305 }
306 return randomString.ToString();
307 }
308
309 /// <summary>
310 /// Returns a random element and removes it from list
311 /// </summary>
312 public static T PopRandom<T>(this List<T> items)
313 {
314 if (items == null)
315 {
316 throw new ArgumentNullException("items");
317 }
318 if (items.Count == 0)
319 {
320 Debug.LogError("Empty array");
321 return default(T);
322 }
323 var index = Random.Range(0, items.Count);
324 var item = items[index];
325 items.RemoveAt(index);
326 return item;
327 }
328
329 /// <summary>
330 /// Fisher–Yates shuffle
331 /// </summary>
332 /// <remarks>
333 /// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle
334 /// </remarks>
335 public static void Shuffle<T>(this T[] array)
336 {
337 if (array == null)
338 {
339 throw new ArgumentNullException("array");
340 }
341 for (int i = 0; i < array.Length; i++)
342 {
343 int j = Random.Range(i, array.Length);
344 T tmp = array[j];
345 array[j] = array[i];
346 array[i] = tmp;
347 }
348 }
349
350 /// <summary>
351 /// Fisher–Yates shuffle
352 /// </summary>
353 /// <remarks>
354 /// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle
355 /// </remarks>
356 public static void Shuffle<T>(this List<T> array)
357 {
358 if (array == null)
359 {
360 throw new ArgumentNullException("array");
361 }
362 for (int i = 0; i < array.Count; i++)
363 {
364 int j = Random.Range(i, array.Count);
365 T tmp = array[j];
366 array[j] = array[i];
367 array[i] = tmp;
368 }
369 }
370
371 /// <summary>
372 /// Returns true with probability from <paramref name="percent"/>
373 /// </summary>
374 /// <param name="percent">between 0.0 [inclusive] and 1.0 [inclusive]</param>
375 public static bool Chance(float percent)
376 {
377 if (percent == 0) return false;
378 if (percent == 1) return true;
379 return Random.value < percent;
380 }
381
382 /// <summary>
383 /// Returns a random point on the perimeter of a square with sides <paramref name="a"/> and <paramref name="b"/>
384 /// </summary>
385 public static Vector2 PointOnSquare(float a, float b)
386 {
387 float value = Random.value*(2*a + 2*b);
388 if (value < a)
389 {
390 return new Vector2(value, 0);
391 }
392 value -= a;
393 if (value < b)
394 {
395 return new Vector2(a, value);
396 }
397 value -= b;
398 if (value < a)
399 {
400 return new Vector2(value, b);
401 }
402 return new Vector2(0, value - a);
403 }
404
405 /// <summary>
406 /// Returns a random point inside <paramref name="bounds"/>
407 /// </summary>
408 public static Vector3 PointInBounds(Bounds bounds)
409 {
410 return Range(bounds.min, bounds.max);
411 }
412
413 /// <summary>
414 /// Returns a random vector between <paramref name="min"/> [inclusive] and <paramref name="max"/> [inclusive]
415 /// </summary>
416 public static Vector2 Range(Vector2 min, Vector2 max)
417 {
418 return new Vector2(Random.Range(min.x, max.x), Random.Range(min.y, max.y));
419 }
420
421 /// <summary>
422 /// Returns a random vector between <paramref name="min"/> [inclusive] and <paramref name="max"/> [inclusive]
423 /// </summary>
424 public static Vector3 Range(Vector3 min, Vector3 max)
425 {
426 return new Vector3(Random.Range(min.x, max.x), Random.Range(min.y, max.y), Random.Range(min.z, max.z));
427 }
428
429 /// <summary>
430 /// Returns a random vector between <paramref name="min"/> [inclusive] and <paramref name="max"/> [inclusive]
431 /// </summary>
432 public static Vector4 Range(Vector4 min, Vector4 max)
433 {
434 return new Vector4(Random.Range(min.x, max.x), Random.Range(min.y, max.y), Random.Range(min.z, max.z),
435 Random.Range(min.w, max.w));
436 }
437
438 /// <summary>
439 /// Returns a random float number between and <paramref name="min"/> [inclusive] and <paramref name="max"/> [inclusive].
440 /// Ensures that there will be only specified amount of variants.
441 /// </summary>
442 public static float Range(float min, float max, int variants)
443 {
444 if (variants < 2)
445 {
446 Debug.LogError("Variants must be greater than one");
447 variants = 2;
448 }
449 return Mathf.Lerp(min, max, Random.Range(0, variants)/(variants - 1f));
450 }
451
452 /// <summary>
453 /// Returns a random vector between and <paramref name="min"/> [inclusive] and <paramref name="max"/> [inclusive].
454 /// Ensures that there will be only specified amount of variants.
455 /// </summary>
456 public static Vector2 Range(Vector2 min, Vector2 max, int variants)
457 {
458 return new Vector2(Range(min.x, max.x, variants), Range(min.y, max.y, variants));
459 }
460
461 /// <summary>
462 /// Returns a random vector between and <paramref name="min"/> [inclusive] and <paramref name="max"/> [inclusive].
463 /// Ensures that there will be only specified amount of variants.
464 /// </summary>
465 public static Vector3 Range(Vector3 min, Vector3 max, int variants)
466 {
467 return new Vector3(Range(min.x, max.x, variants), Range(min.y, max.y, variants),
468 Range(min.z, max.z, variants));
469 }
470
471 /// <summary>
472 /// Returns a random vector between and <paramref name="min"/> [inclusive] and <paramref name="max"/> [inclusive].
473 /// Ensures that there will be only specified amount of variants.
474 /// </summary>
475 public static Vector4 Range(Vector4 min, Vector4 max, int variants)
476 {
477 return new Vector4(Range(min.x, max.x, variants), Range(min.y, max.y, variants),
478 Range(min.z, max.z, variants), Range(min.w, max.w, variants));
479 }
480 }
481 }