using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Barotrauma { static class MathUtils { public static Vector2 SmoothStep(Vector2 v1, Vector2 v2, float amount) { return new Vector2( MathHelper.SmoothStep(v1.X, v2.X, amount), MathHelper.SmoothStep(v1.Y, v2.Y, amount)); } public static float Round(float value, float div) { return (value < 0.0f) ? (float)Math.Ceiling(value / div) * div : (float)Math.Floor(value / div) * div; } public static float RoundTowardsClosest(float value, float div) { return (float)Math.Round(value / div) * div; } public static float VectorToAngle(Vector2 vector) { return (float)Math.Atan2(vector.Y, vector.X); } public static bool IsValid(float value) { return (!float.IsInfinity(value) && !float.IsNaN(value)); } public static bool IsValid(Vector2 vector) { return (IsValid(vector.X) && IsValid(vector.Y)); } public static Rectangle ExpandRect(Rectangle rect, int amount) { return new Rectangle(rect.X - amount, rect.Y + amount, rect.Width + amount * 2, rect.Height + amount * 2); } public static int VectorOrientation(Vector2 p1, Vector2 p2, Vector2 p) { // Determinant float Orin = (p2.X - p1.X) * (p.Y - p1.Y) - (p.X - p1.X) * (p2.Y - p1.Y); if (Orin > 0) return -1; // (* Orientation is to the left-hand side *) if (Orin < 0) return 1; // (* Orientation is to the right-hand side *) return 0; // (* Orientation is neutral aka collinear *) } public static float CurveAngle(float from, float to, float step) { from = WrapAngleTwoPi(from); to = WrapAngleTwoPi(to); if (Math.Abs(from - to) < MathHelper.Pi) { // The simple case - a straight lerp will do. return MathHelper.Lerp(from, to, step); } // If we get here we have the more complex case. // First, increment the lesser value to be greater. if (from < to) from += MathHelper.TwoPi; else to += MathHelper.TwoPi; float retVal = MathHelper.Lerp(from, to, step); // Now ensure the return value is between 0 and 2pi if (retVal >= MathHelper.TwoPi) retVal -= MathHelper.TwoPi; return retVal; } /// /// wrap the angle between 0.0f and 2pi /// public static float WrapAngleTwoPi(float angle) { if (float.IsInfinity(angle) || float.IsNegativeInfinity(angle) || float.IsNaN(angle)) { return 0.0f; } while (angle < 0) angle += MathHelper.TwoPi; while (angle >= MathHelper.TwoPi) angle -= MathHelper.TwoPi; return angle; } /// /// wrap the angle between -pi and pi /// public static float WrapAnglePi(float angle) { if (float.IsInfinity(angle) || float.IsNegativeInfinity(angle) || float.IsNaN(angle)) { return 0.0f; } // Ensure that -pi <= angle < pi for both "from" and "to" while (angle < -MathHelper.Pi) angle += MathHelper.TwoPi; while (angle >= MathHelper.Pi) angle -= MathHelper.TwoPi; return angle; } public static float GetShortestAngle(float from, float to) { // Ensure that 0 <= angle < 2pi for both "from" and "to" from = WrapAngleTwoPi(from); to = WrapAngleTwoPi(to); if (Math.Abs(from - to) < MathHelper.Pi) { return to - from; } // If we get here we have the more complex case. // First, increment the lesser value to be greater. if (from < to) from += MathHelper.TwoPi; else to += MathHelper.TwoPi; return to - from; } /// /// solves the angle opposite to side a (parameters: lengths of each side) /// public static float SolveTriangleSSS(float a, float b, float c) { float A = (float)Math.Acos((b * b + c * c - a * a) / (2 * b * c)); if (float.IsNaN(A)) A = 1.0f; return A; } public static byte AngleToByte(float angle) { angle = WrapAngleTwoPi(angle); angle = angle * (255.0f / MathHelper.TwoPi); return Convert.ToByte(angle); } public static float ByteToAngle(byte b) { float angle = (float)b; angle = angle * (MathHelper.TwoPi / 255.0f); return angle; } /// /// check whether line from a to b is intersecting with line from c to b /// public static bool LinesIntersect(Vector2 a, Vector2 b, Vector2 c, Vector2 d) { float denominator = ((b.X - a.X) * (d.Y - c.Y)) - ((b.Y - a.Y) * (d.X - c.X)); float numerator1 = ((a.Y - c.Y) * (d.X - c.X)) - ((a.X - c.X) * (d.Y - c.Y)); float numerator2 = ((a.Y - c.Y) * (b.X - a.X)) - ((a.X - c.X) * (b.Y - a.Y)); if (denominator == 0) return numerator1 == 0 && numerator2 == 0; float r = numerator1 / denominator; float s = numerator2 / denominator; return (r >= 0 && r <= 1) && (s >= 0 && s <= 1); } // a1 is line1 start, a2 is line1 end, b1 is line2 start, b2 is line2 end public static Vector2? GetLineIntersection(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2) { Vector2 b = a2 - a1; Vector2 d = b2 - b1; float bDotDPerp = b.X * d.Y - b.Y * d.X; // if b dot d == 0, it means the lines are parallel so have infinite intersection points if (bDotDPerp == 0) return null; Vector2 c = b1 - a1; float t = (c.X * d.Y - c.Y * d.X) / bDotDPerp; if (t < 0 || t > 1) return null; float u = (c.X * b.Y - c.Y * b.X) / bDotDPerp; if (u < 0 || u > 1) return null; return a1 + t * b; } public static Vector2? GetLineRectangleIntersection(Vector2 a1, Vector2 a2, Rectangle rect) { Vector2? intersection = GetLineIntersection(a1, a2, new Vector2(rect.X, rect.Y), new Vector2(rect.Right, rect.Y)); if (intersection != null) return intersection; intersection = GetLineIntersection(a1, a2, new Vector2(rect.X, rect.Y-rect.Height), new Vector2(rect.Right, rect.Y-rect.Height)); if (intersection != null) return intersection; intersection = GetLineIntersection(a1, a2, new Vector2(rect.X, rect.Y), new Vector2(rect.X, rect.Y - rect.Height)); if (intersection != null) return intersection; return GetLineIntersection(a1, a2, new Vector2(rect.Right, rect.Y), new Vector2(rect.Right, rect.Y - rect.Height)); } public static float LineToPointDistance(Vector2 lineA, Vector2 lineB, Vector2 point) { return (float)(Math.Abs((lineB.X-lineA.X)*(lineA.Y - point.Y) - (lineA.X - point.X)*(lineB.Y-lineA.Y)) / Math.Sqrt(Math.Pow(lineB.X - lineA.X, 2) + Math.Pow(lineB.Y - lineA.Y, 2))); } public static bool CircleIntersectsRectangle(Vector2 circlePos, float radius, Rectangle rect) { float xDist = Math.Abs(circlePos.X - rect.Center.X); int halfWidth = rect.Width / 2; if (xDist > (halfWidth + radius)) { return false; } if (xDist <= (halfWidth)) { return true; } float yDist = Math.Abs(circlePos.Y - rect.Center.Y); int halfHeight = rect.Height / 2; if (yDist > (halfHeight + radius)) { return false; } if (yDist <= (halfHeight)) { return true; } float distSqX = xDist - halfWidth; float distSqY = yDist - halfHeight; return (distSqX * distSqX + distSqY * distSqY <= (radius * radius)); } /// /// divide a convex hull into triangles /// /// List of triangle vertices (sorted counter-clockwise) public static List TriangulateConvexHull(List vertices, Vector2 center) { List triangles = new List(); int triangleCount = vertices.Count - 2; vertices.Sort(new CompareCCW(center)); int lastIndex = 1; for (int i = 0; i < triangleCount; i++) { Vector2[] triangleVertices = new Vector2[3]; triangleVertices[0] = vertices[0]; int k = 1; for (int j = lastIndex; j <= lastIndex + 1; j++) { triangleVertices[k] = vertices[j]; k++; } lastIndex += 1; triangles.Add(triangleVertices); } return triangles; } public static List GiftWrap(List points) { Vector2 leftMost = points[0]; foreach (Vector2 point in points) { if (point.X < leftMost.X) leftMost = point; } List wrappedPoints = new List(); Vector2 currPoint = leftMost; Vector2 endPoint; do { wrappedPoints.Add(currPoint); endPoint = points[0]; for (int i = 1; i < points.Count; i++) { if (points[i] == currPoint) continue; if (currPoint == endPoint || MathUtils.VectorOrientation(currPoint, endPoint, points[i]) == -1) { endPoint = points[i]; } } currPoint = endPoint; } while (endPoint != leftMost); return wrappedPoints; } public static List GenerateJaggedLine(Vector2 start, Vector2 end, int generations, float offsetAmount) { List segments = new List(); segments.Add(new Vector2[] { start, end }); for (int n = 0; n < generations; n++) { for (int i = 0; i < segments.Count; i++) { Vector2 startSegment = segments[i][0]; Vector2 endSegment = segments[i][1]; segments.RemoveAt(i); Vector2 midPoint = (startSegment + endSegment) / 2.0f; Vector2 normal = Vector2.Normalize(endSegment - startSegment); normal = new Vector2(-normal.Y, normal.X); midPoint += normal * Rand.Range(-offsetAmount, offsetAmount, false); segments.Insert(i, new Vector2[] { startSegment, midPoint }); segments.Insert(i + 1, new Vector2[] { midPoint, endSegment }); i++; } } return segments; } // Returns the human-readable file size for an arbitrary, 64-bit file size // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" public static string GetBytesReadable(long i) { // Get absolute value long absolute_i = (i < 0 ? -i : i); // Determine the suffix and readable value string suffix; double readable; if (absolute_i >= 0x1000000000000000) // Exabyte { suffix = "EB"; readable = (i >> 50); } else if (absolute_i >= 0x4000000000000) // Petabyte { suffix = "PB"; readable = (i >> 40); } else if (absolute_i >= 0x10000000000) // Terabyte { suffix = "TB"; readable = (i >> 30); } else if (absolute_i >= 0x40000000) // Gigabyte { suffix = "GB"; readable = (i >> 20); } else if (absolute_i >= 0x100000) // Megabyte { suffix = "MB"; readable = (i >> 10); } else if (absolute_i >= 0x400) // Kilobyte { suffix = "KB"; readable = i; } else { return i.ToString("0 B"); // Byte } // Divide by 1024 to get fractional value readable = (readable / 1024); // Return formatted number with suffix return readable.ToString("0.# ") + suffix; } } class CompareCCW : IComparer { private Vector2 center; public CompareCCW(Vector2 center) { this.center = center; } public int Compare(Vector2 a, Vector2 b) { return Compare(a, b, center); } public static int Compare(Vector2 a, Vector2 b, Vector2 center) { if (a.X - center.X >= 0 && b.X - center.X < 0) return -1; if (a.X - center.X < 0 && b.X - center.X >= 0) return 1; if (a.X - center.X == 0 && b.X - center.X == 0) { if (a.Y - center.Y >= 0 || b.Y - center.Y >= 0) return Math.Sign(b.Y - a.Y); return Math.Sign(a.Y - b.Y); } // compute the cross product of vectors (center -> a) x (center -> b) float det = (a.X - center.X) * (b.Y - center.Y) - (b.X - center.X) * (a.Y - center.Y); if (det < 0) return -1; if (det > 0) return 1; // points a and b are on the same line from the center // check which point is closer to the center float d1 = (a.X - center.X) * (a.X - center.X) + (a.Y - center.Y) * (a.Y - center.Y); float d2 = (b.X - center.X) * (b.X - center.X) + (b.Y - center.Y) * (b.Y - center.Y); return Math.Sign(d2 - d1); } } }