cb6dcd469e
commit 8a2a718a0544d5c1707f10b75498e528a7912726 Author: Joonas Rikkonen <poe.regalis@gmail.com> Date: Tue Mar 19 16:47:49 2019 +0200 Only update affliction icons every 0.5 seconds (or when switching limb selection in the health interface). Otherwise afflictions can flicker rapidly when the player for example has status effects that cause an affliction's strength to fluctuate around the ShowIconThreshold. Also set the default ShowIconThreshold value to 0.05. Closes #1304
925 lines
32 KiB
C#
925 lines
32 KiB
C#
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using Barotrauma.Extensions;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
//TODO: Currently this is only used for text positioning? -> move there?
|
|
[Flags]
|
|
public enum Alignment
|
|
{
|
|
CenterX = 1, Left = 2, Right = 4, CenterY = 8, Top = 16, Bottom = 32,
|
|
TopLeft = (Top | Left), TopCenter = (CenterX | Top), TopRight = (Top | Right),
|
|
CenterLeft = (Left | CenterY), Center = (CenterX | CenterY), CenterRight = (Right | CenterY),
|
|
BottomLeft = (Bottom | Left), BottomCenter = (CenterX | Bottom), BottomRight = (Bottom | Right),
|
|
}
|
|
|
|
static class MathUtils
|
|
{
|
|
public static float Percentage(float portion, float total)
|
|
{
|
|
return portion / total * 100;
|
|
}
|
|
|
|
public static int PositiveModulo(int i, int n)
|
|
{
|
|
return (i % n + n) % n;
|
|
}
|
|
|
|
public static double Distance(double x1, double y1, double x2, double y2)
|
|
{
|
|
double dX = x1 - x2;
|
|
double dY = y1 - y2;
|
|
return Math.Sqrt(dX * dX + dY * dY);
|
|
}
|
|
|
|
public static double DistanceSquared(double x1, double y1, double x2, double y2)
|
|
{
|
|
double dX = x1 - x2;
|
|
double dY = y1 - y2;
|
|
return dX * dX + dY * dY;
|
|
}
|
|
|
|
public static int DistanceSquared(int x1, int y1, int x2, int y2)
|
|
{
|
|
int dX = x1 - x2;
|
|
int dY = y1 - y2;
|
|
return dX * dX + dY * dY;
|
|
}
|
|
|
|
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 Vector2 ClampLength(this Vector2 v, float length)
|
|
{
|
|
float currLength = v.Length();
|
|
if (currLength > length)
|
|
{
|
|
return v / currLength * length;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
public static bool Contains(this Rectangle rect, double x, double y)
|
|
{
|
|
return x > rect.X && x < rect.Right && y > rect.Y && y < rect.Bottom;
|
|
}
|
|
|
|
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 Point ToPoint(Vector2 vector)
|
|
{
|
|
return new Point((int)vector.X,(int)vector.Y);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// wrap the angle between 0.0f and 2pi
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// wrap the angle between -pi and pi
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the angle between the two angles, where the direction matters.
|
|
/// </summary>
|
|
public static float GetMidAngle(float from, float to)
|
|
{
|
|
float max = MathHelper.Max(from, to);
|
|
float min = MathHelper.Min(from, to);
|
|
float diff = max - min;
|
|
if (from < to)
|
|
{
|
|
// Clockwise
|
|
return from + diff / 2;
|
|
}
|
|
else
|
|
{
|
|
// CCW
|
|
return from - diff / 2;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// solves the angle opposite to side a (parameters: lengths of each side)
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether line from a to b is intersecting with line from c to b
|
|
/// </summary>
|
|
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 bool GetLineIntersection(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, out Vector2 intersection)
|
|
{
|
|
intersection = Vector2.Zero;
|
|
|
|
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 false;
|
|
|
|
Vector2 c = b1 - a1;
|
|
float t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;
|
|
if (t < 0 || t > 1) return false;
|
|
|
|
float u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;
|
|
if (u < 0 || u > 1) return false;
|
|
|
|
intersection = a1 + t * b;
|
|
return true;
|
|
}
|
|
|
|
public static bool GetAxisAlignedLineIntersection(Vector2 a1, Vector2 a2, Vector2 axisAligned1, Vector2 axisAligned2, bool isHorizontal, out Vector2 intersection)
|
|
{
|
|
intersection = Vector2.Zero;
|
|
|
|
if (!isHorizontal)
|
|
{
|
|
float xDiff = axisAligned1.X - a1.X;
|
|
if (Math.Sign(xDiff) == Math.Sign(axisAligned1.X - a2.X)) { return false; }
|
|
|
|
float s = (a2.Y - a1.Y) / (a2.X - a1.X);
|
|
float y = a1.Y + xDiff * s;
|
|
|
|
if (axisAligned1.Y < axisAligned2.Y)
|
|
{
|
|
if (y < axisAligned1.Y) return false;
|
|
if (y > axisAligned2.Y) return false;
|
|
}
|
|
else
|
|
{
|
|
if (y > axisAligned1.Y) return false;
|
|
if (y < axisAligned2.Y) return false;
|
|
}
|
|
|
|
intersection = new Vector2(axisAligned1.X, y);
|
|
return true;
|
|
}
|
|
else //horizontal line
|
|
{
|
|
float yDiff = axisAligned1.Y - a1.Y;
|
|
if (Math.Sign(yDiff) == Math.Sign(axisAligned1.Y - a2.Y)) { return false; }
|
|
|
|
float s = (a2.X - a1.X) / (a2.Y - a1.Y);
|
|
float x = a1.X + yDiff * s;
|
|
|
|
if (axisAligned1.X < axisAligned2.X)
|
|
{
|
|
if (x < axisAligned1.X) return false;
|
|
if (x > axisAligned2.X) return false;
|
|
}
|
|
else
|
|
{
|
|
if (x > axisAligned1.X) return false;
|
|
if (x < axisAligned2.X) return false;
|
|
}
|
|
|
|
intersection = new Vector2(x, axisAligned1.Y);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public static bool GetLineRectangleIntersection(Vector2 a1, Vector2 a2, Rectangle rect, out Vector2 intersection)
|
|
{
|
|
if (GetAxisAlignedLineIntersection(a1, a2,
|
|
new Vector2(rect.X, rect.Y),
|
|
new Vector2(rect.Right, rect.Y),
|
|
true, out intersection))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (GetAxisAlignedLineIntersection(a1, a2,
|
|
new Vector2(rect.X, rect.Y-rect.Height),
|
|
new Vector2(rect.Right, rect.Y-rect.Height),
|
|
true, out intersection))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(GetAxisAlignedLineIntersection(a1, a2,
|
|
new Vector2(rect.X, rect.Y),
|
|
new Vector2(rect.X, rect.Y - rect.Height),
|
|
false, out intersection))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (GetAxisAlignedLineIntersection(a1, a2,
|
|
new Vector2(rect.Right, rect.Y),
|
|
new Vector2(rect.Right, rect.Y - rect.Height),
|
|
false, out intersection))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*public static List<Vector2> GetLineRectangleIntersections(Vector2 a1, Vector2 a2, Rectangle rect)
|
|
{
|
|
List<Vector2> intersections = new List<Vector2>();
|
|
|
|
Vector2? intersection = GetAxisAlignedLineIntersection(a1, a2,
|
|
new Vector2(rect.X, rect.Y),
|
|
new Vector2(rect.Right, rect.Y),
|
|
true);
|
|
|
|
if (intersection != null) intersections.Add((Vector2)intersection);
|
|
|
|
intersection = GetAxisAlignedLineIntersection(a1, a2,
|
|
new Vector2(rect.X, rect.Y - rect.Height),
|
|
new Vector2(rect.Right, rect.Y - rect.Height),
|
|
true);
|
|
|
|
if (intersection != null) intersections.Add((Vector2)intersection);
|
|
|
|
intersection = GetAxisAlignedLineIntersection(a1, a2,
|
|
new Vector2(rect.X, rect.Y),
|
|
new Vector2(rect.X, rect.Y - rect.Height),
|
|
false);
|
|
|
|
if (intersection != null) intersections.Add((Vector2)intersection);
|
|
|
|
intersection = GetAxisAlignedLineIntersection(a1, a2,
|
|
new Vector2(rect.Right, rect.Y),
|
|
new Vector2(rect.Right, rect.Y - rect.Height),
|
|
false);
|
|
|
|
if (intersection != null) intersections.Add((Vector2)intersection);
|
|
|
|
return intersections;
|
|
}*/
|
|
|
|
|
|
/// <summary>
|
|
/// Get the intersections between a line (either infinite or a line segment) and a circle
|
|
/// </summary>
|
|
/// <param name="circlePos">Center of the circle</param>
|
|
/// <param name="radius">Radius of the circle</param>
|
|
/// <param name="point1">1st point on the line</param>
|
|
/// <param name="point2">2nd point on the line</param>
|
|
/// <param name="isLineSegment">Is the line a segment or infinite</param>
|
|
/// <returns>The number of intersections</returns>
|
|
public static int GetLineCircleIntersections(Vector2 circlePos, float radius,
|
|
Vector2 point1, Vector2 point2, bool isLineSegment, out Vector2? intersection1, out Vector2? intersection2)
|
|
{
|
|
float dx, dy, A, B, C, det;
|
|
|
|
dx = point2.X - point1.X;
|
|
dy = point2.Y - point1.Y;
|
|
|
|
A = dx * dx + dy * dy;
|
|
B = 2 * (dx * (point1.X - circlePos.X) + dy * (point1.Y - circlePos.Y));
|
|
C = (point1.X - circlePos.X) * (point1.X - circlePos.X) + (point1.Y - circlePos.Y) * (point1.Y - circlePos.Y) - radius * radius;
|
|
|
|
det = B * B - 4 * A * C;
|
|
if ((A <= 0.0000001) || (det < 0))
|
|
{
|
|
// No real solutions.
|
|
intersection1 = null;
|
|
intersection2 = null;
|
|
return 0;
|
|
}
|
|
else if (det == 0)
|
|
{
|
|
// One solution.
|
|
float t = -B / (2 * A);
|
|
intersection1 = new Vector2(point1.X + t * dx, point1.Y + t * dy);
|
|
intersection2 = null;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
// Two solutions.
|
|
float t1 = (float)((-B + Math.Sqrt(det)) / (2 * A));
|
|
float t2 = (float)((-B - Math.Sqrt(det)) / (2 * A));
|
|
|
|
//if the line is not infinite, we need to check if the intersections are on the segment
|
|
if (isLineSegment)
|
|
{
|
|
if (t1 >= 0 && t1 <= 1.0f)
|
|
{
|
|
intersection1 = point1 + new Vector2(dx, dy) * t1;
|
|
if (t2 >= 0 && t2 <= 1.0f)
|
|
{
|
|
//both intersections on the segment
|
|
intersection2 = point1 + new Vector2(dx, dy) * t2;
|
|
return 2;
|
|
|
|
}
|
|
//only the first intersection is on the segment
|
|
intersection2 = null;
|
|
return 1;
|
|
}
|
|
else if (t2 >= 0 && t2 <= 1.0f)
|
|
{
|
|
//only the second intersection is on the segment
|
|
intersection1 = point1 + new Vector2(dx, dy) * t2;
|
|
intersection2 = null;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
//neither is on the segment
|
|
intersection1 = null;
|
|
intersection2 = null;
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
intersection1 = point1 + new Vector2(dx, dy) * t1;
|
|
intersection2 = point1 + new Vector2(dx, dy) * t2;
|
|
return 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static float LineToPointDistance(Vector2 lineA, Vector2 lineB, Vector2 point)
|
|
{
|
|
float xDiff = lineB.X - lineA.X;
|
|
float yDiff = lineB.Y - lineA.Y;
|
|
|
|
if (xDiff == 0 && yDiff == 0)
|
|
{
|
|
return Vector2.Distance(lineA, point);
|
|
}
|
|
|
|
return (float)(Math.Abs(xDiff * (lineA.Y - point.Y) - yDiff * (lineA.X - point.X)) /
|
|
Math.Sqrt(xDiff * xDiff + yDiff * yDiff));
|
|
}
|
|
|
|
public static bool CircleIntersectsRectangle(Vector2 circlePos, float radius, Rectangle rect)
|
|
{
|
|
int halfWidth = rect.Width / 2;
|
|
float xDist = Math.Abs(circlePos.X - (rect.X + halfWidth));
|
|
if (xDist > halfWidth + radius) { return false; }
|
|
|
|
int halfHeight = rect.Height / 2;
|
|
float yDist = Math.Abs(circlePos.Y - (rect.Y + halfHeight));
|
|
if (yDist > halfHeight + radius) { return false; }
|
|
|
|
if (xDist <= halfWidth || yDist <= halfHeight) { return true; }
|
|
|
|
float distSqX = xDist - halfWidth;
|
|
float distSqY = yDist - halfHeight;
|
|
|
|
return distSqX * distSqX + distSqY * distSqY <= radius * radius;
|
|
}
|
|
|
|
/// <summary>
|
|
/// divide a convex hull into triangles
|
|
/// </summary>
|
|
/// <returns>List of triangle vertices (sorted counter-clockwise)</returns>
|
|
public static List<Vector2[]> TriangulateConvexHull(List<Vector2> vertices, Vector2 center)
|
|
{
|
|
List<Vector2[]> triangles = new List<Vector2[]>();
|
|
|
|
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<Vector2> GiftWrap(List<Vector2> points)
|
|
{
|
|
if (points.Count == 0) return points;
|
|
|
|
Vector2 leftMost = points[0];
|
|
foreach (Vector2 point in points)
|
|
{
|
|
if (point.X < leftMost.X) leftMost = point;
|
|
}
|
|
|
|
List<Vector2> wrappedPoints = new List<Vector2>();
|
|
|
|
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<Vector2[]> GenerateJaggedLine(Vector2 start, Vector2 end, int iterations, float offsetAmount)
|
|
{
|
|
List<Vector2[]> segments = new List<Vector2[]>();
|
|
|
|
segments.Add(new Vector2[] { start, end });
|
|
|
|
for (int n = 0; n < iterations; 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, Rand.RandSync.Server);
|
|
|
|
segments.Insert(i, new Vector2[] { startSegment, midPoint });
|
|
segments.Insert(i + 1, new Vector2[] { midPoint, endSegment });
|
|
|
|
i++;
|
|
}
|
|
|
|
offsetAmount *= 0.5f;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public static void SplitRectanglesHorizontal(List<Rectangle> rects, Vector2 point)
|
|
{
|
|
for (int i = 0; i < rects.Count; i++)
|
|
{
|
|
if (point.Y > rects[i].Y && point.Y < rects[i].Y + rects[i].Height)
|
|
{
|
|
Rectangle rect1 = rects[i];
|
|
Rectangle rect2 = rects[i];
|
|
|
|
rect1.Height = (int)(point.Y - rects[i].Y);
|
|
|
|
rect2.Height = rects[i].Height - rect1.Height;
|
|
rect2.Y = rect1.Y + rect1.Height;
|
|
rects[i] = rect1;
|
|
rects.Insert(i + 1, rect2); i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void SplitRectanglesVertical(List<Rectangle> rects, Vector2 point)
|
|
{
|
|
for (int i = 0; i < rects.Count; i++)
|
|
{
|
|
if (point.X>rects[i].X && point.X<rects[i].X+rects[i].Width)
|
|
{
|
|
Rectangle rect1 = rects[i];
|
|
Rectangle rect2 = rects[i];
|
|
|
|
rect1.Width = (int)(point.X-rects[i].X);
|
|
|
|
rect2.Width = rects[i].Width - rect1.Width;
|
|
rect2.X = rect1.X + rect1.Width;
|
|
rects[i] = rect1;
|
|
rects.Insert(i + 1, rect2); i++;
|
|
}
|
|
}
|
|
|
|
/*for (int i = 0; i < rects.Count; i++)
|
|
{
|
|
if (rects[i].Width <= 0 || rects[i].Height <= 0)
|
|
{
|
|
rects.RemoveAt(i); i--;
|
|
}
|
|
}*/
|
|
}
|
|
|
|
/// <summary>
|
|
/// Float comparison. Note that may still fail in some cases.
|
|
/// </summary>
|
|
// References:
|
|
// http://floating-point-gui.de/errors/comparison/
|
|
// https://stackoverflow.com/questions/3874627/floating-point-comparison-functions-for-c-sharp
|
|
public static bool NearlyEqual(float a, float b, float epsilon = 0.0001f)
|
|
{
|
|
float diff = Math.Abs(a - b);
|
|
if (a == b)
|
|
{
|
|
// shortcut, handles infinities
|
|
return true;
|
|
}
|
|
else if (a == 0 || b == 0 || diff < float.Epsilon)
|
|
{
|
|
// a or b is zero or both are extremely close to it
|
|
// relative error is less meaningful here
|
|
return diff < epsilon;
|
|
}
|
|
else
|
|
{
|
|
// use relative error
|
|
return diff / (Math.Abs(a) + Math.Abs(b)) < epsilon;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Float comparison. Note that may still fail in some cases.
|
|
/// </summary>
|
|
public static bool NearlyEqual(Vector2 a, Vector2 b, float epsilon = 0.0001f)
|
|
{
|
|
return NearlyEqual(a.X, b.X, epsilon) && NearlyEqual(a.Y, b.Y, epsilon);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a position in a curve.
|
|
/// </summary>
|
|
public static Vector2 Bezier(Vector2 start, Vector2 control, Vector2 end, float t)
|
|
{
|
|
return Pow(1 - t, 2) * start + 2 * t * (1 - t) * control + Pow(t, 2) * end;
|
|
}
|
|
|
|
public static float Pow(float f, float p)
|
|
{
|
|
return (float)Math.Pow(f, p);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rotates a point in 2d space around another point.
|
|
/// Modified from:
|
|
/// http://www.gamefromscratch.com/post/2012/11/24/GameDev-math-recipes-Rotating-one-point-around-another-point.aspx
|
|
/// </summary>
|
|
public static Vector2 RotatePointAroundTarget(Vector2 point, Vector2 target, float degrees, bool clockWise = true)
|
|
{
|
|
// (Math.PI / 180) * degrees
|
|
var angle = MathHelper.ToRadians(degrees);
|
|
var sin = Math.Sin(angle);
|
|
var cos = Math.Cos(angle);
|
|
if (!clockWise)
|
|
{
|
|
sin = -sin;
|
|
}
|
|
Vector2 dir = point - target;
|
|
var x = (cos * dir.X) - (sin * dir.Y) + target.X;
|
|
var y = (sin * dir.X) + (cos * dir.Y) + target.Y;
|
|
return new Vector2((float)x, (float)y);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the corners of an imaginary rectangle.
|
|
/// Unlike the XNA rectangle, this can be rotated with the up parameter.
|
|
/// </summary>
|
|
public static Vector2[] GetImaginaryRect(Vector2 up, Vector2 center, Vector2 size)
|
|
{
|
|
return GetImaginaryRect(new Vector2[4], up, center, size);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the corners of an imaginary rectangle.
|
|
/// Unlike the XNA Rectangle, this can be rotated with the up parameter.
|
|
/// </summary>
|
|
public static Vector2[] GetImaginaryRect(Vector2[] corners, Vector2 up, Vector2 center, Vector2 size)
|
|
{
|
|
if (corners.Length != 4)
|
|
{
|
|
throw new Exception("Invalid length for the corners array. Must be 4.");
|
|
}
|
|
Vector2 halfSize = size / 2;
|
|
Vector2 left = up.Right();
|
|
corners[0] = center + up * halfSize.Y + left * halfSize.X;
|
|
corners[1] = center + up * halfSize.Y - left * halfSize.X;
|
|
corners[2] = center - up * halfSize.Y - left * halfSize.X;
|
|
corners[3] = center - up * halfSize.Y + left * halfSize.X;
|
|
return corners;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if a point is inside a rectangle.
|
|
/// Unlike the XNA Rectangle, this rectangle might have been rotated.
|
|
/// For XNA Rectangles, use the Contains instance method.
|
|
/// </summary>
|
|
public static bool RectangleContainsPoint(Vector2[] corners, Vector2 point)
|
|
{
|
|
if (corners.Length != 4)
|
|
{
|
|
throw new Exception("Invalid length of the corners array! Must be 4");
|
|
}
|
|
return RectangleContainsPoint(corners[0], corners[1], corners[2], corners[3], point);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if a point is inside a rectangle.
|
|
/// Unlike the XNA Rectangle, this rectangle might have been rotated.
|
|
/// For XNA Rectangles, use the Contains instance method.
|
|
/// </summary>
|
|
public static bool RectangleContainsPoint(Vector2 c1, Vector2 c2, Vector2 c3, Vector2 c4, Vector2 point)
|
|
{
|
|
return TriangleContainsPoint(c1, c2, c3, point) || TriangleContainsPoint(c1, c3, c4, point);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Slightly modified from https://gamedev.stackexchange.com/questions/110229/how-do-i-efficiently-check-if-a-point-is-inside-a-rotated-rectangle
|
|
/// </summary>
|
|
public static bool TriangleContainsPoint(Vector2 c1, Vector2 c2, Vector2 c3, Vector2 point)
|
|
{
|
|
// Compute vectors
|
|
Vector2 v0 = c3 - c1;
|
|
Vector2 v1 = c2 - c1;
|
|
Vector2 v2 = point - c1;
|
|
|
|
// Compute dot products
|
|
float dot00 = Vector2.Dot(v0, v0);
|
|
float dot01 = Vector2.Dot(v0, v1);
|
|
float dot02 = Vector2.Dot(v0, v2);
|
|
float dot11 = Vector2.Dot(v1, v1);
|
|
float dot12 = Vector2.Dot(v1, v2);
|
|
|
|
// Compute barycentric coordinates
|
|
float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
|
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
|
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
|
|
|
// Check if the point is in triangle
|
|
return u >= 0 && v >= 0 && (u + v) < 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a scalar t from a value v between a range from min to max. Clamped between 0 and 1.
|
|
/// </summary>
|
|
public static float InverseLerp(float min, float max, float v)
|
|
{
|
|
float diff = max - min;
|
|
// Ensure that we don't get division by zero exceptions.
|
|
if (diff == 0) { return v >= max ? 1f : 0f; }
|
|
return MathHelper.Clamp((v - min) / diff, 0f, 1f);
|
|
}
|
|
}
|
|
|
|
class CompareCCW : IComparer<Vector2>
|
|
{
|
|
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 == b) return 0;
|
|
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);
|
|
}
|
|
}
|
|
}
|