using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; namespace Otter { /// /// Main utility function class. Various useful functions for 2d game development and Otter stuff. /// public static class Util { #region Constants /// /// Right /// public const float RIGHT = 0; /// /// Up /// public const float UP = (float)Math.PI * -.5f; /// /// Left /// public const float LEFT = (float)Math.PI; /// /// Down /// public const float DOWN = (float)Math.PI * .5f; /// /// Up Right /// public const float UP_RIGHT = (float)Math.PI * -.25f; /// /// Up Left /// public const float UP_LEFT = (float)Math.PI * -.75f; /// /// Down Right /// public const float DOWN_RIGHT = (float)Math.PI * .25f; /// /// Down Left /// public const float DOWN_LEFT = (float)Math.PI * .75f; /// /// Degrees to Radians /// public const float DEG_TO_RAD = (float)Math.PI / 180f; /// /// Radians to Degrees /// public const float RAD_TO_DEG = 180f / (float)Math.PI; private const string HEX = "0123456789ABCDEF"; #endregion #region Static Methods /// /// A shortcut function to send text to the debugger log. /// /// The string to send. public static void Log(object str) { if (Debugger.Instance == null) return; Debugger.Instance.Log(str); } /// /// A shortcut function to send text to the debugger log. /// /// The tag to log with. /// The string to send. public static void LogTag(string tag, object str) { if (Debugger.Instance == null) return; Debugger.Instance.Log(tag, str); } public static void LogTag(string tag, string str, params object[] obj) { if (Debugger.Instance == null) return; Debugger.Instance.Log(tag, string.Format(str, obj)); } public static void Log(string str, params object[] obj) { if (Debugger.Instance == null) return; Debugger.Instance.Log("", string.Format(str, obj)); } /// /// Summon the Debugger. /// public static void ShowDebugger() { if (Debugger.Instance == null) return; Debugger.Instance.Summon(); } /// /// A shortcut function to watch a value in the debugger. This must be called every update to /// keep the value updated (not an automatic watch.) /// /// The name of the value. /// The value. public static void Watch(string str, object obj) { if (Debugger.Instance == null) return; Debugger.Instance.Watch(str, obj); } /// /// Interpolate between two values. /// /// The first value. /// The second value. /// The progress of the interpolation. /// The interpolated value. public static float Lerp(float a, float b, float t = 1) { return a + (b - a) * t; } /// /// Interpolate through a set of numbers. /// /// The amount of completion of the lerp. (0 - 1) /// The numbers to interpolate through. /// The interpolated number. public static float LerpSet(float amount, params float[] numbers) { if (amount <= 0) return numbers[0]; if (amount >= 1) return numbers[numbers.Length - 1]; int fromIndex = (int)Util.ScaleClamp(amount, 0, 1, 0, numbers.Length - 1); int toIndex = fromIndex + 1; float length = 1f / (numbers.Length - 1); float lerp = Util.ScaleClamp(amount % length, 0, length, 0, 1); // This is a fix for odd numbered color amounts. When fromIndex was // odd, lerp would evaluate to 1 when it should be 0. if (lerp >= 0.9999f && fromIndex % 2 == 1) { lerp = 0; } return Lerp(numbers[fromIndex], numbers[toIndex], lerp); } /// /// Interpolate through a looping set of numbers. /// /// The amount of completion of the lerp. (0 - 1) /// The numbers to interpolate through. /// The interpolated number. public static float LerpSetLoop(float amount, params float[] numbers) { //convert numbers to looping set List set = new List(); List numberSet = new List(numbers); numberSet.Add(numbers[0]); for (var i = 0; i < numberSet.Count; i++) { var current = numberSet[i]; set.Add(current); if (i + 1 < numberSet.Count) { var next = numberSet[i + 1]; var nextSet = (current + next) / 2f; set.Add(nextSet); } } return LerpSet(amount, set.ToArray()); } /// /// Interpolate from one Color to another. /// /// The start Color. /// The end Color. /// The amount of completion on the lerp. (0 - 1) /// The interpolated Color. public static Color LerpColor(Color from, Color to, float amount) { if (amount <= 0) return new Color(from); if (amount >= 1) return new Color(to); var c = new Color(from); c.R = from.R + (to.R - from.R) * amount; c.G = from.G + (to.G - from.G) * amount; c.B = from.B + (to.B - from.B) * amount; c.A = from.A + (to.A - from.A) * amount; return c; } /// /// Interpolate through a set of Colors. /// /// The amount of completion on the lerp. (0 - 1) /// The Colors to interpolate through. /// The interpolated Color. public static Color LerpColor(float amount, params Color[] colors) { if (amount <= 0) return colors[0]; if (amount >= 1) return colors[colors.Length - 1]; int fromIndex = (int)Util.ScaleClamp(amount, 0, 1, 0, colors.Length - 1); int toIndex = fromIndex + 1; float length = 1f / (colors.Length - 1); float lerp = Util.ScaleClamp(amount % length, 0, length, 0, 1); // This is a fix for odd numbered color amounts. When fromIndex was // odd, lerp would evaluate to 1 when it should be 0. if (lerp >= 0.9999f && fromIndex % 2 == 1) { lerp = 0; } return LerpColor(colors[fromIndex], colors[toIndex], lerp); } /// /// Clamps a value inside a range. /// /// The value to clamp. /// Min clamp. /// Max clamp. /// The new value between min and max. public static float Clamp(float value, float min, float max) { if (value < min) return min; if (value > max) return max; return value; } /// /// Clamps a value inside a range. /// /// The value to clamp. /// Min clamp. /// Max clamp. /// The new value between min and max. public static Vector2 Clamp(Vector2 value, Vector2 min, Vector2 max) { return new Vector2( Util.Clamp(value.X, min.X, max.X), Util.Clamp(value.Y, min.Y, max.Y) ); } /// /// Clamps a value inside a range. /// /// The value to clamp. /// Max clamp. /// The new value between 0 and max. public static Vector2 Clamp(Vector2 value, Vector2 max) { return Clamp(value, Vector2.Zero, max); } /// /// Clamps a value inside a range. /// /// The value to clamp. /// Max clamp /// The new value between 0 and max. public static float Clamp(float value, float max) { return Clamp(value, 0, max); } /// /// Clamps a value inside a range. /// /// The value to clamp. /// The range. /// The clamped value in the range. public static float Clamp(float value, Range range) { return Clamp(value, range.Min, range.Max); } /// /// Steps an angle value toward a target based on a certain amount. /// /// The angle value to step. /// The target value to approach. /// The amount to approach by. /// The new angle value approaching the target from 0 to 360. public static float ApproachAngle(float from, float to, float amount) { var sign = AngleDifferenceSign(from, to); var diff = AngleDifference(from, to); var maxMove = Math.Min(Math.Abs(diff), amount); return (from + maxMove * sign); } /// /// Steps a value toward a target based on a certain amount. /// /// The value to step. /// The target to approach. /// The maximum increment toward the target. /// The new value approaching the target. static public float Approach(float val, float target, float maxMove) { return val > target ? Math.Max(val - maxMove, target) : Math.Min(val + maxMove, target); } /// /// Steps a value toward a target based on a certain amount. /// /// The value to step. /// The target to approach. /// The maximum increment toward the target. /// The new value approaching the target. static public Vector2 Approach(Vector2 val, Vector2 target, Vector2 maxMove) { return new Vector2( Approach(val.X, target.X, maxMove.X), Approach(val.Y, target.Y, maxMove.Y) ); } static public Vector2 Approach(Vector2 val, Vector2 target, float maxMove) { return Approach(val, target, new Vector2(maxMove, maxMove)); } /// /// Snaps a value to the nearest value on a grid. /// /// The input value. /// The size of each grid space. /// The offset to apply after the snap. /// The snapped value. static public float SnapToGrid(float value, float increment, float offset = 0) { return ((float)Math.Floor(value / increment) * increment) + offset; } /// /// Converts a hex character to a byte. /// /// The input character. /// The byte. static public byte HexToByte(char c) { return (byte)HEX.IndexOf(char.ToUpper(c)); } /// /// Get the minimum value from a set of values. /// /// The values to test. /// The minimum value. static public float Min(params float[] values) { float min = values[0]; for (int i = 1; i < values.Length; i++) min = Math.Min(values[i], min); return min; } /// /// Get the maximum value from a set of values. /// /// The values to test. /// The maximum value. static public float Max(params float[] values) { float max = values[0]; for (int i = 1; i < values.Length; i++) max = Math.Max(values[i], max); return max; } /// /// Convert a number in a range of min, max to a number in the range min2, max2. /// Also known as RemapRange in some frameworks. /// /// The input value. /// The original minimum. /// The original maximum. /// The new minimum. /// The new maximum. /// A value scaled from the original min and max to the new min and max. public static float Scale(float value, float min, float max, float min2, float max2) { return min2 + ((value - min) / (max - min)) * (max2 - min2); } /// /// Convert a number in range of min, max to a number in the range min2, max2, but also /// clamp the result inside min2 and max2. /// /// The input value. /// The original minimum. /// The original maximum. /// The new minimum. /// The new maximum. /// A value scaled from the original min and max to the new min and max, and clamped to the new min and max. public static float ScaleClamp(float value, float min, float max, float min2, float max2) { value = min2 + ((value - min) / (max - min)) * (max2 - min2); if (max2 > min2) { value = value < max2 ? value : max2; return value > min2 ? value : min2; } value = value < min2 ? value : min2; return value > max2 ? value : max2; } /// /// Shortcut to Scale the value from a sine wave. Original min and max are -1 and 1. /// /// The input value to sine. /// The new minimum. /// The new maximum. /// A value scaled from -1 and 1 to the new min and max. public static float SinScale(float value, float min, float max) { return Scale((float)Math.Sin(value * DEG_TO_RAD), -1f, 1f, min, max); } /// /// Shortcut to ScaleClamp the value from a sine wave. Original min and max are -1 and 1. /// /// The input value to sine. /// The new minimum. /// The new maximum. /// A value scaled from -1 and 1 to the new min and max, and clamped to the new min and max. public static float SinScaleClamp(float value, float min, float max) { return ScaleClamp((float)Math.Sin(value * DEG_TO_RAD), -1f, 1f, min, max); } /// /// Round down. /// /// The value. /// The value rounded down. public static float Floor(float value) { return (float)Math.Floor(value); } /// /// Round up. /// /// The value. /// The value rounded up. public static float Ceil(float value) { return (float)Math.Ceiling(value); } /// /// Round. /// /// The value. /// The value rounded. public static float Round(float value) { return (float)Math.Round(value); } /// /// The angle of a x and y coordinate. /// /// The X position. /// The Y position. /// An angle between 0 - 360 degrees. public static float Angle(float x, float y) { //y is negative since y is DOWN in video games //return degrees by default return (float)Math.Atan2(-y, x) * Util.RAD_TO_DEG; } /// /// Get the angle between two Entities. /// /// The first Entity. /// The second Entity. /// The angle between the two Entity's positions between 0 - 360 degrees. public static float Angle(Entity e1, Entity e2) { return Angle(e1.X, e1.Y, e2.X, e2.Y); } /// /// The angle of a vector. /// /// The vector. /// An angle between 0 - 360 degrees. public static float Angle(Vector2 vector) { return Angle((float)vector.X, (float)vector.Y); } /// /// Get the angle between two vectors. /// /// The first vector. /// The second vector. /// The angle between the two vectors. public static float Angle(Vector2 from, Vector2 to) { return Angle((float)(to.X - from.X), (float)(to.Y - from.Y)); } /// /// Get the angle between two positions. /// /// The first X position. /// The first Y position. /// The second X position. /// The second Y position. /// The angle between the two positions. public static float Angle(float x1, float y1, float x2, float y2) { return Angle(x2 - x1, y2 - y1); } /// /// Get the difference between two angles from -180 to 180. /// /// The first angle. /// The second angle. /// The difference between the angles from -180 to 180. public static float AngleDifference(float a, float b) { var diff = b - a; while (diff > 180) { diff -= 360; } while (diff <= -180) { diff += 360; } return diff; } /// /// Get the shortest direction from angle a to angle b. /// /// The first angle. /// The second angle.. /// 1 for clockwise, -1 for counter clockwise, 0 if angles are the same. public static int AngleDifferenceSign(float a, float b) { if (a == b) return 0; var dif = AngleDifference(a, b); return (int)Math.Sign(dif); } /// /// Rotate a position by an angle. /// /// The position to rotate. /// The amount to rotate the position in degrees. /// The new rotated position. public static Vector2 Rotate(Vector2 vector, float amount) { return Rotate(vector.X, vector.Y, amount); } /// /// Rotate a position by an angle. /// /// The X position to rotate. /// The Y position to rotate. /// The amount to rotate the position in degrees. /// The new rotated position. public static Vector2 Rotate(float x, float y, float amount) { amount *= -1; // Flip Y because of video game coordinates. return new Vector2(x * Cos(amount) - y * Sin(amount), x * Sin(amount) + y * Cos(amount)); // Wow this is fancy /* // old rotate code just in case I broke something var v = new Vector2(x, y); var length = v.Length; var angle = Angle(x, y) + amount; v.X = PolarX(angle, length); v.Y = PolarY(angle, length); return v; */ } /// /// Rotate a position by an angle around an anchor point. /// /// The X position to rotate. /// The Y position to rotate. /// The X position to rotate around. /// The Y position to rotate around. /// The amount to rotate the position in degrees. /// The new rotated position. public static Vector2 RotateAround(float x, float y, float aroundX, float aroundY, float amount) { var vec = Rotate(x - aroundX, y - aroundY, amount); vec.X += aroundX; vec.Y += aroundY; return vec; } /// /// Rotate a position by an angle around an anchor point. /// /// The position to rotate. /// The position to rotate around. /// The amount to rotate the position in degrees. /// The new rotated position. public static Vector2 RotateAround(Vector2 point, Vector2 around, float amount) { return RotateAround(point.X, point.Y, around.X, around.Y, amount); } /// /// Scale a position by an amount around an anchor point. /// /// The X position to scale. /// The Y position to scale. /// The X position to scale around. /// The Y position to scale around. /// The X amount to scale by. /// The Y amount to scale by. /// The new scaled position. public static Vector2 ScaleAround(float x, float y, float aroundX, float aroundY, float amountX, float amountY) { x -= aroundX; y -= aroundY; x *= amountX; y *= amountY; x += aroundX; y += aroundY; return new Vector2(x, y); } /// /// Scale a position by an amount around an anchor point. /// /// The position to scale. /// The position to scale around. /// The X amount to scale by. /// The Y amount to scale by. /// The new scaled position. public static Vector2 ScaleAround(Vector2 point, Vector2 around, float amountX, float amountY) { return ScaleAround(point.X, point.Y, around.X, around.Y, amountX, amountY); } /// /// Scale a position by an amount around an anchor point. /// /// The position to scale. /// The position to scale around. /// The amount to scale by. /// The new scaled position. public static Vector2 ScaleAround(Vector2 point, Vector2 around, float amount) { return ScaleAround(point, around, amount, amount); } /// /// Scale a position by an amount around an anchor point. /// /// The position to scale. /// The position to scale around. /// The amount to scale by. /// The new scaled position. public static Vector2 ScaleAround(Vector2 point, Vector2 around, Vector2 amount) { return ScaleAround(point.X, point.Y, around.X, around.Y, amount.X, amount.Y); } /// /// Distance check. /// /// The first X position. /// The first Y position. /// The second X position. /// The second Y position. /// The distance between the two points. public static float Distance(float x1, float y1, float x2, float y2) { return (float)Math.Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); } /// /// Distance check. /// /// The first position. /// The second position. /// The distance between the two positions. public static float Distance(Vector2 from, Vector2 to) { return Distance(from.X, from.Y, to.X, to.Y); } /// /// Distance check. /// /// The first Entity. /// The second Entity. /// The distance between the Entities. public static float Distance(Entity e1, Entity e2) { return Distance(e1.X, e1.Y, e2.X, e2.Y); } /// /// Check for a point in a rectangle defined by min and max points. /// /// The point to check. /// The top left corner of the rectangle. /// The bottom right corner of the rectangle. /// True if the point is in the rectangle. public static bool InRect(Vector2 p, Vector2 min, Vector2 max) { return p.X > min.X && p.Y > min.Y && p.X < max.X && p.Y < max.Y; } /// /// Check for a point in a rectangle. /// /// The X position of the point to check. /// The Y position of the point to check. /// The rectangle. /// True if the point is in the rectangle. public static bool InRect(float x, float y, Rectangle rect) { if (x <= rect.X) return false; if (x >= rect.X + rect.Width) return false; if (y <= rect.Y) return false; if (y >= rect.Y + rect.Height) return false; return true; } /// /// Check for a point in a rectangle. /// /// The X position of the point to check. /// The Y position of the point to check. /// The left of the rectangle. /// The top of the rectangle. /// The width of the rectangle. /// The height of the rectangle. /// True if the point is in the rectangle. public static bool InRect(float x, float y, float rx, float ry, float rw, float rh) { if (x <= rx) return false; if (x >= rx + rw) return false; if (y <= ry) return false; if (y >= ry + rh) return false; return true; } /// /// Check for a point in a rectangle. /// /// The X and Y position of the point to check. /// The rectangle. /// True if the point is in the rectangle. public static bool InRect(Vector2 xy, Rectangle rect) { return InRect((float)xy.X, (float)xy.Y, rect); } /// /// Check for a point inside of a circle. /// /// The point to check. /// The center point of the circle. /// The radius of the circle. /// True if the point is inside the circle. public static bool InCircle(Vector2 p, Vector2 circleP, float radius) { return Distance((float)p.X, (float)p.Y, (float)circleP.X, (float)circleP.Y) <= radius; } /// /// Check for a point inside of a circle. /// /// The X position to check. /// The Y position to check. /// The center X position of the circle. /// The center Y position of the check. /// The radius of the circle. /// True if the point is inside the circle. public static bool InCircle(float x, float y, float circleX, float circleY, float radius) { return Distance(x, y, circleX, circleY) <= radius; } /// /// Check an intersection between two rectangles. /// /// The left of the first rectangle. /// The top of the first rectangle. /// The width of the first rectangle. /// The height of the first rectangle. /// The left of the second rectangle. /// The top of the second rectangle. /// The width of the second rectangle. /// The height of the second rectangle. /// True if the rectangles intersect. public static bool IntersectRectangles(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2) { if (x1 + w1 <= x2) return false; if (x1 >= x2 + w2) return false; if (y1 + h1 <= y2) return false; if (y1 >= y2 + h2) return false; return true; } /// /// The X component of a vector represented by an angle and radius. /// /// The angle of the vector. /// The length of the vector. /// The X component. public static float PolarX(float angle, float length) { return Cos(angle) * length; } /// /// The Y component of a vector represented by an angle and radius. /// /// The angle of the vector. /// The length of the vector. /// The Y component. public static float PolarY(float angle, float length) { //Radius is negative since Y positive is DOWN in video games. return Sin(angle) * -length; } /// /// Wrapper for the sin function that uses degrees. /// /// The angle. /// The sine of the angle. public static float Sin(float degrees) { return (float)Math.Sin(degrees * DEG_TO_RAD); } /// /// Wrapper for the cos function that uses degrees. /// /// The angle. /// The cosine of the angle. public static float Cos(float degrees) { return (float)Math.Cos(degrees * DEG_TO_RAD); } /// /// Convert a two dimensional position to a one dimensional index. /// /// The width of the two dimensional set. /// The X position in the two dimensional set. /// The Y position in the two dimensional set. /// The one dimensional index in a two dimensional set. public static int OneDee(int width, int x, int y) { return y * width + x; } /// /// Convert a one dimensional index to a two dimensional position. /// /// The one dimensional index in the two dimensional set. /// The width of the two dimensional set. /// The X and Y position in the two dimensional set. public static Vector2 TwoDee(int index, int width) { if (width == 0) { return new Vector2(index, 0); } return new Vector2(index % width, index / width); } /// /// The X position from converting an index to a two dimensional position. /// /// The one dimensional index in the two dimensional set. /// The width of the two dimensional set. /// The X position in the two dimensional set. public static int TwoDeeX(int index, int width) { return (int)TwoDee(index, width).X; } /// /// The Y position from converting an index to a two dimensional position. /// /// The one dimensional index in the two dimensional set. /// The width of the two dimensional set. /// The Y position in the two dimensional set. public static int TwoDeeY(int index, int width) { return (int)TwoDee(index, width).Y; } /// /// Normal vector. /// /// The angle. /// The normal vector of that angle. public static Vector2 Normal(float angle) { return new Vector2(Cos(angle), Sin(angle)); } /// /// Checks if an object contains a method. /// /// The object to check for the method on. /// The name of the method to check for. /// True if the object has that method. public static bool HasMethod(object objectToCheck, string methodName) { var type = objectToCheck.GetType(); return type.GetMethod(methodName) != null || type.BaseType.GetMethod(methodName) != null; } /// /// Checks if an object contains a property. /// /// The object to check for the property on. /// The name of the property. /// True if the object has that property. public static bool HasProperty(object objectToCheck, string propertyName) { var type = objectToCheck.GetType(); return type.GetProperty(propertyName) != null || type.BaseType.GetProperty(propertyName) != null; } /// /// Get the value of a property from an object. /// /// The object to get the property from. /// The name of the property. /// The property value. public static object GetPropValue(object source, string propName) { return source.GetType().GetProperty(propName).GetValue(source, null); } /// /// Checks to see if an object has a field by name. /// /// The object to check for the field on. /// The name of the field. /// True if the object has that property. public static bool HasField(object objectToCheck, string fieldName) { return objectToCheck.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public) != null; } /// /// Get the value of a field by name from an object. /// /// The object to get the field from. /// The name of the field. /// The value of the field. public static object GetFieldValue(object source, string fieldName) { return source.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).GetValue(source); } /// /// Get the static value of a field by name from an object. /// /// The type to look for the field in. /// The name of the static field. /// The value of the static field. public static object GetFieldValue(Type type, string fieldName) { return type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).GetValue(null); } /// /// Get the value of a field by name from an object with a default return. /// /// The object to get the field from. /// The name of the field. /// The value to return if the field is not found. /// The field value or the value to return if the field is not found. public static object GetFieldValue(object src, string fieldName, object returnOnNull) { if (src == null) return returnOnNull; if (!Util.HasField(src, fieldName)) return returnOnNull; return GetFieldValue(src, fieldName); } /// /// Set the value of a field on an object by name. /// /// The object to set the field on. /// The name of the field. /// The new value of the field. public static void SetFieldValue(object src, string fieldName, object value) { var fi = src.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); fi.SetValue(src, value); } /// /// Checks if a value is in a specified range. /// /// The value to check. /// The minimum. /// The maximum. /// True if the value is in the range. public static bool InRange(float value, float min, float max) { if (value >= min && value <= max) return true; return false; } /// /// Returns a value according to a value's position in a range. /// /// The value to check. /// The minimum. /// The maximum. /// 0 if the value is in the set. -1 if the value is below the minimum. 1 if the value is above the maximum. public static int Subset(float value, float min, float max) { if (value < min) return -1; if (value > max) return 1; return 0; } /// /// Wrap and angle and keep it within the range of 0 to 360. /// /// The angle. /// The angle wrapped to be in the range of 0 to 360. public static float WrapAngle(float angle) { angle %= 360; if (angle > 180) return angle - 360; else if (angle <= -180) return angle + 360; else return angle; } /// /// Find the distance between a point and a rectangle. /// /// The X position of the point. /// The Y position of the point. /// The X position of the rectangle. /// The Y position of the rectangle. /// The width of the rectangle. /// The height of the rectangle. /// The distance. Returns 0 if the point is within the rectangle. public static float DistanceRectPoint(float px, float py, float rx, float ry, float rw, float rh) { if (px >= rx && px <= rx + rw) { if (py >= ry && py <= ry + rh) return 0; if (py > ry) return py - (ry + rh); return ry - py; } if (py >= ry && py <= ry + rh) { if (px > rx) return px - (rx + rw); return rx - px; } if (px > rx) { if (py > ry) return Distance(px, py, rx + rw, ry + rh); return Distance(px, py, rx + rw, ry); } if (py > ry) return Distance(px, py, rx, ry + rh); return Distance(px, py, rx, ry); } /// /// Find the distance between a point and a rectangle. /// /// The X position of the point. /// The Y position of the point. /// The rectangle. /// The distance. Returns 0 if the point is within the rectangle. public static float DistanceRectPoint(float px, float py, Rectangle rect) { return DistanceRectPoint(px, py, rect.X, rect.Y, rect.Width, rect.Height); } /// /// Convert XML attributes to a dictionary. /// /// The attributes to convert. /// A dictionary of string, string with the attribute names and values. public static Dictionary XmlAttributesToDictionary(XmlAttributeCollection attributes) { var d = new Dictionary(); foreach (XmlAttribute attr in attributes) { d.Add(attr.Name, attr.Value); } return d; } /// /// Searches all known assemblies for a type and returns that type. /// /// The type to search for. /// The type found. Null if no match. public static Type GetTypeFromAllAssemblies(string type, bool ignoreCase = false) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblies) { var types = assembly.GetTypes(); foreach (var t in types) { if (t.Name == type) { return t; } if (ignoreCase) { if (t.Name.ToLower() == type) { return t; } } } } return null; } /// /// Get the list of all types in all known assemblies. /// /// The list of all types in all known assemblies. public static List GetTypesFromAllAssemblies() { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var allTypes = new List(); foreach (var assembly in assemblies) { var types = assembly.GetTypes(); foreach (var t in types) { allTypes.Add(t); } } return allTypes; } /// /// Get the basic type name of an object. /// /// The object. /// The basic type name of the object. public static string GetBasicTypeName(object obj) { var strarr = obj.GetType().ToString().Split('.'); return strarr[strarr.Length - 1]; } /// /// Compresses a string and base64 encodes it. Use "DecompressString" to restore it. /// /// The string to compress. /// The compressed string. public static string CompressString(string str) { var bytes = Encoding.UTF8.GetBytes(str); using (var msi = new MemoryStream(bytes)) using (var mso = new MemoryStream()) { using (var gs = new GZipStream(mso, CompressionMode.Compress)) { CopyStream(msi, gs); } return Convert.ToBase64String(mso.ToArray()); } } /// /// Copies a stream from source to destination. /// /// The string to copy. /// The stream to copy the string to. public static void CopyStream(Stream src, Stream dest) { byte[] bytes = new byte[4096]; int cnt; while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0) { dest.Write(bytes, 0, cnt); } } /// /// Decompresses a string compressed with "CompressString" /// /// The compressed string. /// The uncompressed string. public static string DecompressString(string base64str) { byte[] bytes; try { bytes = Convert.FromBase64String(base64str); } catch { return null; } using (var msi = new MemoryStream(bytes)) using (var mso = new MemoryStream()) { using (var gs = new GZipStream(msi, CompressionMode.Decompress)) { CopyStream(gs, mso); } return Encoding.UTF8.GetString(mso.ToArray()); } } /// /// Compresses a byte array using GZip /// /// The data to compress /// The compressed byte array public static byte[] CompressBytes(byte[] data) { using (var compressedStream = new MemoryStream()) using (var zipStream = new GZipStream(compressedStream, CompressionLevel.Optimal)) { zipStream.Write(data, 0, data.Length); zipStream.Close(); return compressedStream.ToArray(); } } /// /// Decompresses a byte array using GZip /// /// The data to decompress /// The decompressed byte array public static byte[] DecompressBytes(byte[] data) { using (var compressedStream = new MemoryStream(data)) using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress)) using (var resultStream = new MemoryStream()) { zipStream.CopyTo(resultStream); return resultStream.ToArray(); } } /// /// Converts a dictionary of string, string into a string of data. /// /// The dictionary. /// The string that separates keys. /// The string that separates values. /// A string with all of the dictionary's data. public static string DictionaryToString(Dictionary dictionary, string keydelim, string valuedelim) { string str = ""; foreach (var s in dictionary) { str += s.Key + keydelim + s.Value + valuedelim; } str = str.Substring(0, str.Length - valuedelim.Length); return str; } /// /// Convert a string into a dictionary of string, string. /// /// The string to convert into a dictionary. /// The string that separates keys. /// The string that separates values. /// A dictionary with all the string's data. public static Dictionary StringToDictionary(string source, string keydelim, string valuedelim) { var d = new Dictionary(); string[] split = Regex.Split(source, valuedelim); foreach (var s in split) { string[] entry = Regex.Split(s, keydelim); d.Add(entry[0], entry[1]); } return d; } /// /// Calculate an MD5 hash of a string. /// /// The string to calculate the has for. /// The MD5 hash of the string. public static string MD5Hash(string input) { // step 1, calculate MD5 hash from input MD5 md5 = System.Security.Cryptography.MD5.Create(); byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input); byte[] hash = md5.ComputeHash(inputBytes); // step 2, convert byte array to hex string StringBuilder sb = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { sb.Append(hash[i].ToString("X2")); } return sb.ToString(); } /// /// Get all the values of an enum without a giant mess of code. /// /// The type of the Enum. /// An enumerable containing all the enum values. public static IEnumerable EnumValues() { return Enum.GetValues(typeof(T)).Cast(); } /// /// Convert a string number to a float. If the string contains a % char it will be parsed as a percentage. /// For example "50%" => 0.5f, or 50 => 50f. /// /// The string to parse. /// If the string contained % a float of the percent on the scale of 0 to 1. Otherwise the float. public static float ParsePercent(string percent) { if (percent.Contains('%')) { percent = percent.TrimEnd('%'); return float.Parse(percent) * 0.01f; } return float.Parse(percent); } /// /// Download data from a URL. This method will stall the thread. Probably /// should use something like a BackgroundWorker or something but I haven't /// figured that out yet. /// /// The url to download. /// The string downloaded from the url. public static string GetURLString(string url) { string s = ""; using (WebClient client = new WebClient()) { s = client.DownloadString(url); } return s; } /// /// Parse an enum value from a string. /// /// The type of the enum. /// The string to parse. /// The enum value. public static T ParseEnum(string str) { return (T)Enum.Parse(typeof(T), str); } /// /// Convert a generic enum's value to a string. /// /// The enum value. /// The string of the enum value. public static string EnumValueToString(Enum value) { if (enumStringCache.ContainsKey(value)) return enumStringCache[value]; var str = string.Format("{0}.{1}", value.GetType(), value); enumStringCache.Add(value, str); return enumStringCache[value]; } static Dictionary enumStringCache = new Dictionary(); /// /// Convert a generic enum's value into a string of just its value. (No type included!) /// /// The enum value. /// The value of the enum following the final period. public static string EnumValueToBasicString(Enum e) { var split = Util.EnumValueToString(e).Split('.'); return split[split.Length - 1]; } /// /// Convert an array of generic enums to ints. /// /// The enums to convert. /// An array of ints. public static int[] EnumToIntArray(params Enum[] enums) { int[] ints = new int[enums.Length]; for (var i = 0; i < enums.Length; i++) { ints[i] = Convert.ToInt32(enums[i]); } return ints; } public static int[] EnumToIntArray(List enums) { int[] ints = new int[enums.Count]; for (var i = 0; i < enums.Count; i++) { ints[i] = Convert.ToInt32(enums[i]); } return ints; } /// /// Distance between a line and a point. /// /// The X position of the point. /// The Y position of the point. /// The line. /// The distance from the point to the line. public static float DistanceLinePoint(float x, float y, Line2 line) { return (DistanceLinePoint(x, y, line.X1, line.Y1, line.X2, line.Y2)); } /// /// Distance between a line and a point. /// /// The X position of the point. /// The Y position of the point. /// The first X position of the line. /// The first Y position of the line. /// The second X position of the line. /// The second Y position of the line. /// The distance from the point to the line. public static float DistanceLinePoint(float x, float y, float x1, float y1, float x2, float y2) { if (x1 == x2 && y1 == y2) return Util.Distance(x, y, x1, y1); var px = x2 - x1; var py = y2 - y1; float something = px * px + py * py; var u = ((x - x1) * px + (y - y1) * py) / something; if (u > 1) u = 1; if (u < 0) u = 0; var xx = x1 + u * px; var yy = y1 + u * py; var dx = xx - x; var dy = yy - y; return (float)Math.Sqrt(dx * dx + dy * dy); } /// /// Gets every possible combination of a List. /// /// The type of object. /// A list of objects. /// An IEnumerable of every possible combination in the List. public static IEnumerable> GetPowerSet(List list) { return from m in Enumerable.Range(0, 1 << list.Count) select from i in Enumerable.Range(0, list.Count) where (m & (1 << i)) != 0 select list[i]; } /// /// Shortcut to serialize an object to a file. /// /// The type of object. /// The object to serialize. /// The file to write to. public static void SerializeToFile(T obj, string path) { Stream stream = File.Open(path, FileMode.Create); BinaryFormatter bformatter = new BinaryFormatter(); bformatter.Serialize(stream, obj); stream.Close(); } /// /// Shortcut to deserialize an object from a file. /// /// The type of object. /// The file to read from. /// The deserialized object. public static T DeserializeFromFile(string path) { Stream stream = File.Open(path, FileMode.Open); BinaryFormatter bformatter = new BinaryFormatter(); var obj = (T)bformatter.Deserialize(stream); stream.Close(); return obj; } /// /// Calculates the point in a cubic Bezier curve at t (0 to 1) using 4 control points. /// /// Distance along the curve 0 to 1. /// The first control point. /// The second control point. /// The third control point. /// The fourth control point. /// A Vector2 point along the curve at distance t. public static Vector2 BezierCurvePoint(float t, Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) { // http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/ float u = 1 - t; float uu = u * u; float uuu = uu * u; float tt = t * t; float ttt = tt * t; Vector2 p = uuu * p0; // first term p += 3 * uu * t * p1; // second term p += 3 * u * tt * p2; // third term p += ttt * p3; // fourth term return p; } /// /// Find the point on a Bezier path at t. /// /// The progress along the path 0 to 1. /// The points that make up the cubic Bezier path. (4 points per curve, 1 point of overlap.) /// The position on the curve at t. static public Vector2 BezierPathPoint(float t, params Vector2[] points) { var length = points.Length - (points.Length - 1) % 3; var steps = length / 3; var step = (int)Math.Floor(ScaleClamp(t, 0, 1, 0, steps)); int i = step * 3; var p0 = points[i]; var p1 = points[i + 1]; var p2 = points[i + 2]; var p3 = points[i + 3]; var segment = 1 / (float)(steps); var st = ScaleClamp(t - segment * step, 0, segment, 0, 1); return BezierCurvePoint(st, p0, p1, p2, p3); } static float CatmullRom(float a, float b, float c, float d, float t) { // Hermite Spline... I think? return 0.5f * (2 * b + (c - a) * t + (2 * a - 5 * b + 4 * c - d) * (t * t) + (3 * b - a - 3 * c + d) * (t * t * t)); } /// /// Gets the spline path point. /// /// The progress on the path from 0 to 1. /// The set of points making the path. /// The position on the spline path for the progress t. public static Vector2 SplinePathPoint(float t, params Vector2[] path) { // Shoutouts to Chevy Ray int b = (int)((path.Length - 1) * t); int a = b - 1; int c = b + 1; int d = c + 1; a = (int)Util.Clamp(a, 0, path.Length - 1); b = (int)Util.Clamp(b, 0, path.Length - 1); c = (int)Util.Clamp(c, 0, path.Length - 1); d = (int)Util.Clamp(d, 0, path.Length - 1); float i = 1f / (path.Length - 1); t = (t - b * i) / i; return new Vector2( CatmullRom(path[a].X, path[b].X, path[c].X, path[d].X, t), CatmullRom(path[a].Y, path[b].Y, path[c].Y, path[d].Y, t) ); } /// /// The Width of the desktop. Note that this is for the display that the game was initialized in. /// public static int DesktopWidth { get { return (int)SFML.Window.VideoMode.DesktopMode.Width; } } /// /// The height of the desktop. Note that this is for the display that the game was initialized in. /// public static int DesktopHeight { get { return (int)SFML.Window.VideoMode.DesktopMode.Height; } } /// /// Combines Lists of the same type into one list. Does not remove duplicates. /// /// The type of object. /// The Lists to combine. /// One List to rule them all. public static List ListCombine(params List[] lists) { if (lists.Length == 0) return null; if (lists.Length == 1) return lists[0]; var list = lists[0]; for (int i = 1; i < lists.Length; i++) { list.AddRange(lists[i]); } return list; } /// /// Get all types with a specific Attribute. /// /// The type of Attribute. /// Check inherited classes. /// All types with the Attribute T. public static IEnumerable GetTypesWithAttribute(bool inherit = true) where T : Attribute { return from a in AppDomain.CurrentDomain.GetAssemblies() from t in a.GetTypes() where t.IsDefined(typeof(T), inherit) select t; } /// /// Download the json feed of a Google Spreadsheet from the key and sheet id. /// Only works on publicly shared sheets. /// /// The long id of the sheet. Usually a big set of numbers and letters in the url. /// The sheet id. Usually a shorter set of letters and numbers. Default is "od6". /// A json feed of the spreadsheet. public static string GetSpreadsheetJsonById(string docKey, string sheetId = "od6") { var url = string.Format("https://spreadsheets.google.com/feeds/list/{0}/{1}/public/values?alt=json", docKey, sheetId); return DownloadData(url); } /// /// Download the json feed of a Google Spreadsheet from the key and the sheet name. /// Only works on publicly shared sheets. /// /// The long id of the sheet. Usually a big set of numbers and letters in the url. /// The name of the sheet as it appears on the bottom left of the spreadsheet window. /// A json feed of the spreadsheet. public static string GetSpreadsheetJsonByName(string docKey, string sheetName) { return GetSpreadsheetJsonById(docKey, GetSpreadsheetId(docKey, sheetName)); } /// /// Get the internal id of a Google Spreadsheet sheet. /// Only works on publicly shared sheets. /// /// The long id of the sheet. Usually a big set of numbers and letters in the url. /// The name of the sheet as it appears on the bottom left of the spreadsheet window. /// The internal id of the sheet. public static string GetSpreadsheetId(string docKey, string sheetName) { var url = string.Format("https://spreadsheets.google.com/feeds/worksheets/{0}/public/full", docKey); var xml = DownloadData(url); XElement xDoc = XElement.Parse(xml); var ns = "{" + xDoc.GetDefaultNamespace() + "}"; var id = "od6"; xDoc.Elements(ns + "entry") .Where(x => x.Element(ns + "title").Value == sheetName) .Each(x => x.Elements(ns + "id") .Each(xx => { id = xx.Value.Split('/').Last(); })); return id; } /// /// Get the internal ids of all sheets in a Google Spreadsheet. /// Only works on publicly shared sheets. /// /// The long id of the sheet. Usually a big set of numbers and letters in the url. /// A dictionary with sheet names as the keys, and ids as the values. public static Dictionary GetSpreadsheetIds(string docKey) { var url = string.Format("https://spreadsheets.google.com/feeds/worksheets/{0}/public/full", docKey); var xml = DownloadData(url); XElement xDoc = XElement.Parse(xml); var ns = "{" + xDoc.GetDefaultNamespace() + "}"; var dict = new Dictionary(); var name = ""; var id = ""; xDoc.Elements(ns + "entry") .Each(e => { name = e.Element(ns + "title").Value; id = e.Element(ns + "id").Value.Split('/').Last(); dict.Add(name, id); }); return dict; } // Download url cache stuff static string downloadData; static string downloadUrl = ""; static string DownloadData(string url) { if (downloadUrl != url) { downloadData = new WebClient().DownloadString(url); downloadUrl = url; } return downloadData; } #endregion } }