Barotrauma Client Doc
BarotraumaShared/SharedSource/Utils/MathUtils.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Collections.Generic;
5 using System.Linq;
6 
7 namespace Barotrauma
8 {
9  //TODO: Currently this is only used for text positioning? -> move there?
10  [Flags]
11  public enum Alignment
12  {
13  CenterX = 1, Left = 2, Right = 4, CenterY = 8, Top = 16, Bottom = 32,
14  TopLeft = (Top | Left), TopCenter = (CenterX | Top), TopRight = (Top | Right),
16  BottomLeft = (Bottom | Left), BottomCenter = (CenterX | Bottom), BottomRight = (Bottom | Right),
17  Any = Left | Right | Top | Bottom | Center
18  }
19 
20  static class MathUtils
21  {
22  public static Vector2 DiscardZ(this Vector3 vector)
23  => new Vector2(vector.X, vector.Y);
24 
25  public static float Percentage(float portion, float total)
26  {
27  return portion / total * 100;
28  }
29 
30  public static int PositiveModulo(int i, int n)
31  {
32  return (i % n + n) % n;
33  }
34 
35  public static float PositiveModulo(float i, float n)
36  {
37  return (i % n + n) % n;
38  }
39 
40  public static double Distance(double x1, double y1, double x2, double y2)
41  {
42  double dX = x1 - x2;
43  double dY = y1 - y2;
44  return Math.Sqrt(dX * dX + dY * dY);
45  }
46 
47  public static double DistanceSquared(double x1, double y1, double x2, double y2)
48  {
49  double dX = x1 - x2;
50  double dY = y1 - y2;
51  return dX * dX + dY * dY;
52  }
53 
54  public static int DistanceSquared(int x1, int y1, int x2, int y2)
55  {
56  int dX = x1 - x2;
57  int dY = y1 - y2;
58  return dX * dX + dY * dY;
59  }
60 
61  public static Vector2 SmoothStep(Vector2 v1, Vector2 v2, float amount)
62  {
63  return new Vector2(
64  MathHelper.SmoothStep(v1.X, v2.X, amount),
65  MathHelper.SmoothStep(v1.Y, v2.Y, amount));
66  }
67 
68  public static float SmoothStep(float t)
69  {
70  return t * t * (3f - 2f * t);
71  }
72 
73  public static float SmootherStep(float t)
74  {
75  return t * t * t * (t * (6f * t - 15f) + 10f);
76  }
77 
78  public static float EaseIn(float t)
79  {
80  return 1f - (float)Math.Cos(t * MathHelper.PiOver2);
81  }
82 
83  public static float EaseOut(float t)
84  {
85  return (float)Math.Sin(t * MathHelper.PiOver2);
86  }
87 
88  public static Vector2 ClampLength(this Vector2 v, float length)
89  {
90  float currLength = v.Length();
91  if (currLength > length)
92  {
93  return v / currLength * length;
94  }
95  return v;
96  }
97 
98  public static bool Contains(this Rectangle rect, double x, double y)
99  {
100  return x > rect.X && x < rect.Right && y > rect.Y && y < rect.Bottom;
101  }
102 
103  public static float Round(float value, float div)
104  {
105  return (value < 0.0f) ?
106  (float)Math.Ceiling(value / div) * div :
107  (float)Math.Floor(value / div) * div;
108  }
109 
110  public static int RoundToInt(float v) => (int)MathF.Round(v);
111 
112  public static float RoundTowardsClosest(float value, float div)
113  {
114  return (float)Math.Round(value / div) * div;
115  }
116 
117  public static float VectorToAngle(Vector2 vector)
118  {
119  return (float)Math.Atan2(vector.Y, vector.X);
120  }
121 
122  public static Point ToPoint(Vector2 vector)
123  {
124  return new Point((int)vector.X,(int)vector.Y);
125  }
126 
127  public static bool IsValid(float value)
128  {
129  return (!float.IsInfinity(value) && !float.IsNaN(value));
130  }
131 
132  public static bool IsValid(Vector2 vector)
133  {
134  return (IsValid(vector.X) && IsValid(vector.Y));
135  }
136 
137  public static Rectangle ExpandRect(Rectangle rect, int amount)
138  {
139  return new Rectangle(rect.X - amount, rect.Y + amount, rect.Width + amount * 2, rect.Height + amount * 2);
140  }
141 
148  public static int VectorOrientation(Vector2 pointA, Vector2 pointB, Vector2 pointC)
149  {
150  float determinant = (pointB.X - pointA.X) * (pointC.Y - pointA.Y) - (pointC.X - pointA.X) * (pointB.Y - pointA.Y);
151 
152  return -Math.Sign(determinant);
153  }
154 
155 
156  public static float CurveAngle(float from, float to, float step)
157  {
158  from = WrapAngleTwoPi(from);
159  to = WrapAngleTwoPi(to);
160 
161  if (Math.Abs(from - to) < MathHelper.Pi)
162  {
163  // The simple case - a straight lerp will do.
164  return MathHelper.Lerp(from, to, step);
165  }
166 
167  // If we get here we have the more complex case.
168  // First, increment the lesser value to be greater.
169  if (from < to)
170  from += MathHelper.TwoPi;
171  else
172  to += MathHelper.TwoPi;
173 
174  float retVal = MathHelper.Lerp(from, to, step);
175 
176  // Now ensure the return value is between 0 and 2pi
177  if (retVal >= MathHelper.TwoPi)
178  retVal -= MathHelper.TwoPi;
179  return retVal;
180  }
181 
185  public static float WrapAngleTwoPi(float angle)
186  {
187  if (float.IsInfinity(angle) || float.IsNegativeInfinity(angle) || float.IsNaN(angle))
188  {
189  return 0.0f;
190  }
191  return PositiveModulo(angle, MathHelper.TwoPi);
192  }
193 
197  public static float WrapAnglePi(float angle)
198  {
199  if (float.IsInfinity(angle) || float.IsNegativeInfinity(angle) || float.IsNaN(angle))
200  {
201  return 0.0f;
202  }
203  float min = -MathHelper.Pi;
204  float diffFromMin = angle - min;
205  return diffFromMin - (MathF.Floor(diffFromMin / MathHelper.TwoPi) * MathHelper.TwoPi) + min;
206  }
207 
208  public static float GetShortestAngle(float from, float to)
209  {
210  // Ensure that 0 <= angle < 2pi for both "from" and "to"
211  from = WrapAngleTwoPi(from);
212  to = WrapAngleTwoPi(to);
213 
214  if (Math.Abs(from - to) < MathHelper.Pi)
215  {
216  return to - from;
217  }
218 
219  // If we get here we have the more complex case.
220  // First, increment the lesser value to be greater.
221  if (from < to)
222  from += MathHelper.TwoPi;
223  else
224  to += MathHelper.TwoPi;
225 
226  return to - from;
227  }
228 
232  public static float GetMidAngle(float from, float to)
233  {
234  float max = Math.Max(from, to);
235  float min = Math.Min(from, to);
236  float diff = max - min;
237  if (from < to)
238  {
239  // Clockwise
240  return from + diff / 2;
241  }
242  else
243  {
244  // CCW
245  return from - diff / 2;
246  }
247  }
248 
252  public static float SolveTriangleSSS(float a, float b, float c)
253  {
254  float A = (float)Math.Acos((b * b + c * c - a * a) / (2 * b * c));
255 
256  if (float.IsNaN(A)) A = 1.0f;
257 
258  return A;
259  }
260 
261  public static byte AngleToByte(float angle)
262  {
263  angle = WrapAngleTwoPi(angle);
264  angle = angle * (255.0f / MathHelper.TwoPi);
265  return Convert.ToByte(angle);
266  }
267 
268  public static float ByteToAngle(byte b)
269  {
270  float angle = (float)b;
271  angle = angle * (MathHelper.TwoPi / 255.0f);
272  return angle;
273  }
274 
278  public static bool LineSegmentsIntersect(Vector2 a, Vector2 b, Vector2 c, Vector2 d)
279  {
280  float denominator = ((b.X - a.X) * (d.Y - c.Y)) - ((b.Y - a.Y) * (d.X - c.X));
281  float numerator1 = ((a.Y - c.Y) * (d.X - c.X)) - ((a.X - c.X) * (d.Y - c.Y));
282  float numerator2 = ((a.Y - c.Y) * (b.X - a.X)) - ((a.X - c.X) * (b.Y - a.Y));
283 
284  if (denominator == 0) return numerator1 == 0 && numerator2 == 0;
285 
286  float r = numerator1 / denominator;
287  float s = numerator2 / denominator;
288 
289  return (r >= 0 && r <= 1) && (s >= 0 && s <= 1);
290  }
291 
295  public static bool GetLineSegmentIntersection(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, out Vector2 intersection)
296  {
297  return GetLineIntersection(a1, a2, b1, b2, areLinesInfinite: false, out intersection);
298  }
299 
303  public static bool GetLineIntersection(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, bool areLinesInfinite, out Vector2 intersection)
304  {
305  intersection = Vector2.Zero;
306 
307  Vector2 b = a2 - a1;
308  Vector2 d = b2 - b1;
309  float bDotDPerp = b.X * d.Y - b.Y * d.X;
310 
311  // if b dot d == 0, it means the lines are parallel so have infinite intersection points
312  if (bDotDPerp == 0) return false;
313 
314  Vector2 c = b1 - a1;
315  float t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;
316 
317  if (!areLinesInfinite)
318  {
319  if (t < 0 || t > 1) { return false; }
320  float u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;
321  if (u < 0 || u > 1) { return false; }
322  }
323 
324  intersection = a1 + t * b;
325  return true;
326  }
327 
328  public static bool GetAxisAlignedLineIntersection(Vector2 a1, Vector2 a2, Vector2 axisAligned1, Vector2 axisAligned2, bool isHorizontal, out Vector2 intersection)
329  {
330  intersection = Vector2.Zero;
331 
332  if (!isHorizontal)
333  {
334  float xDiff = axisAligned1.X - a1.X;
335  if (Math.Sign(xDiff) == Math.Sign(axisAligned1.X - a2.X)) { return false; }
336 
337  float s = (a2.Y - a1.Y) / (a2.X - a1.X);
338  float y = a1.Y + xDiff * s;
339 
340  if (axisAligned1.Y < axisAligned2.Y)
341  {
342  if (y < axisAligned1.Y) { return false; }
343  if (y > axisAligned2.Y) { return false; }
344  }
345  else
346  {
347  if (y > axisAligned1.Y) { return false; }
348  if (y < axisAligned2.Y) { return false; }
349  }
350 
351  intersection = new Vector2(axisAligned1.X, y);
352  return true;
353  }
354  else //horizontal line
355  {
356  float yDiff = axisAligned1.Y - a1.Y;
357  if (Math.Sign(yDiff) == Math.Sign(axisAligned1.Y - a2.Y)) { return false; }
358 
359  float s = (a2.X - a1.X) / (a2.Y - a1.Y);
360  float x = a1.X + yDiff * s;
361 
362  if (axisAligned1.X < axisAligned2.X)
363  {
364  if (x < axisAligned1.X) { return false; }
365  if (x > axisAligned2.X) { return false; }
366  }
367  else
368  {
369  if (x > axisAligned1.X) { return false; }
370  if (x < axisAligned2.X) { return false; }
371  }
372 
373  intersection = new Vector2(x, axisAligned1.Y);
374  return true;
375  }
376  }
377 
378  public static bool GetLineRectangleIntersection(Vector2 a1, Vector2 a2, Rectangle rect, out Vector2 intersection)
379  {
380  if (GetAxisAlignedLineIntersection(a1, a2,
381  new Vector2(rect.X, rect.Y),
382  new Vector2(rect.Right, rect.Y),
383  true, out intersection))
384  {
385  return true;
386  }
387 
388  if (GetAxisAlignedLineIntersection(a1, a2,
389  new Vector2(rect.X, rect.Y - rect.Height),
390  new Vector2(rect.Right, rect.Y - rect.Height),
391  true, out intersection))
392  {
393  return true;
394  }
395 
396  if (GetAxisAlignedLineIntersection(a1, a2,
397  new Vector2(rect.X, rect.Y),
398  new Vector2(rect.X, rect.Y - rect.Height),
399  false, out intersection))
400  {
401  return true;
402  }
403 
404  if (GetAxisAlignedLineIntersection(a1, a2,
405  new Vector2(rect.Right, rect.Y),
406  new Vector2(rect.Right, rect.Y - rect.Height),
407  false, out intersection))
408  {
409  return true;
410  }
411 
412  return false;
413  }
414 
415  public static Vector2 FlipX(this Vector2 vector)
416  => new Vector2(-vector.X, vector.Y);
417 
418  public static Vector2 FlipY(this Vector2 vector)
419  => new Vector2(vector.X, -vector.Y);
420 
421  public static Vector2 YX(this Vector2 vector)
422  => new Vector2(x: vector.Y, y: vector.X);
423 
424  public static Point FlipY(this Point point)
425  => new Point(point.X, -point.Y);
426 
427  public static Point YX(this Point point)
428  => new Point(x: point.Y, y: point.X);
429 
430  public static Vector2 RotatedUnitXRadians(float radians)
431  => new Vector2(MathF.Cos(radians), MathF.Sin(radians));
432 
433  public static Vector2 RotatedUnitYRadians(float radians)
434  => RotatedUnitXRadians(radians).YX().FlipX();
435 
436  public static Vector2 Round(this Vector2 vector)
437  => new Vector2((int)MathF.Round(vector.X), (int)MathF.Round(vector.Y));
438 
448  public static int GetLineCircleIntersections(Vector2 circlePos, float radius,
449  Vector2 point1, Vector2 point2, bool isLineSegment, out Vector2? intersection1, out Vector2? intersection2)
450  {
451  float dx, dy, A, B, C, det;
452 
453  dx = point2.X - point1.X;
454  dy = point2.Y - point1.Y;
455 
456  A = dx * dx + dy * dy;
457  B = 2 * (dx * (point1.X - circlePos.X) + dy * (point1.Y - circlePos.Y));
458  C = (point1.X - circlePos.X) * (point1.X - circlePos.X) + (point1.Y - circlePos.Y) * (point1.Y - circlePos.Y) - radius * radius;
459 
460  det = B * B - 4 * A * C;
461  if ((A <= 0.0000001) || (det < 0))
462  {
463  // No real solutions.
464  intersection1 = null;
465  intersection2 = null;
466  return 0;
467  }
468  else if (det == 0)
469  {
470  // One solution.
471  float t = -B / (2 * A);
472  intersection1 = new Vector2(point1.X + t * dx, point1.Y + t * dy);
473  intersection2 = null;
474  return 1;
475  }
476  else
477  {
478  // Two solutions.
479  float t1 = (float)((-B + Math.Sqrt(det)) / (2 * A));
480  float t2 = (float)((-B - Math.Sqrt(det)) / (2 * A));
481 
482  //if the line is not infinite, we need to check if the intersections are on the segment
483  if (isLineSegment)
484  {
485  if (t1 >= 0 && t1 <= 1.0f)
486  {
487  intersection1 = point1 + new Vector2(dx, dy) * t1;
488  if (t2 >= 0 && t2 <= 1.0f)
489  {
490  //both intersections on the segment
491  intersection2 = point1 + new Vector2(dx, dy) * t2;
492  return 2;
493 
494  }
495  //only the first intersection is on the segment
496  intersection2 = null;
497  return 1;
498  }
499  else if (t2 >= 0 && t2 <= 1.0f)
500  {
501  //only the second intersection is on the segment
502  intersection1 = point1 + new Vector2(dx, dy) * t2;
503  intersection2 = null;
504  return 1;
505  }
506  else
507  {
508  //neither is on the segment
509  intersection1 = null;
510  intersection2 = null;
511  return 0;
512  }
513  }
514  else
515  {
516  intersection1 = point1 + new Vector2(dx, dy) * t1;
517  intersection2 = point1 + new Vector2(dx, dy) * t2;
518  return 2;
519  }
520  }
521  }
522 
523  public static float LineToPointDistance(Vector2 lineA, Vector2 lineB, Vector2 point)
524  {
525  float xDiff = lineB.X - lineA.X;
526  float yDiff = lineB.Y - lineA.Y;
527 
528  if (xDiff == 0 && yDiff == 0)
529  {
530  return Vector2.Distance(lineA, point);
531  }
532 
533  return (float)(Math.Abs(xDiff * (lineA.Y - point.Y) - yDiff * (lineA.X - point.X)) /
534  Math.Sqrt(xDiff * xDiff + yDiff * yDiff));
535  }
536 
537  public static float LineToPointDistanceSquared(Vector2 lineA, Vector2 lineB, Vector2 point)
538  {
539  float xDiff = lineB.X - lineA.X;
540  float yDiff = lineB.Y - lineA.Y;
541 
542  if (xDiff == 0 && yDiff == 0)
543  {
544  return Vector2.DistanceSquared(lineA, point);
545  }
546 
547  float numerator = xDiff * (lineA.Y - point.Y) - yDiff * (lineA.X - point.X);
548  return (numerator * numerator) / (xDiff * xDiff + yDiff * yDiff);
549  }
550 
551  public static double LineSegmentToPointDistanceSquared(Point lineA, Point lineB, Point point)
552  {
553  return LineSegmentToPointDistanceSquared(lineA.X, lineA.Y, lineB.X, lineB.Y, point.X, point.Y);
554  }
555 
556  public static float LineSegmentToPointDistanceSquared(Vector2 lineA, Vector2 lineB, Vector2 point)
557  {
558  return (float)LineSegmentToPointDistanceSquared(lineA.X, lineA.Y, lineB.X, lineB.Y, point.X, point.Y);
559  }
560 
561  private static double LineSegmentToPointDistanceSquared(double line1X, double line1Y, double line2X, double line2Y, double pointX, double pointY)
562  {
563  double xDiff = line2X - line1X;
564  double yDiff = line2Y - line1Y;
565 
566  if (xDiff == 0 && yDiff == 0)
567  {
568  double v1 = line1X - pointX;
569  double v2 = line1Y - pointY;
570  return (v1 * v1) + (v2 * v2);
571  }
572 
573  // Calculate the t that minimizes the distance.
574  double t = ((pointX - line1X) * xDiff + (pointY - line1Y) * yDiff) / (xDiff * xDiff + yDiff * yDiff);
575 
576  // See if this represents one of the segment's
577  // end points or a point in the middle.
578  if (t < 0)
579  {
580  xDiff = pointX - line1X;
581  yDiff = pointY - line1Y;
582  }
583  else if (t > 1)
584  {
585  xDiff = pointX - line2X;
586  yDiff = pointY - line2Y;
587  }
588  else
589  {
590  xDiff = pointX - (line1X + t * xDiff);
591  yDiff = pointY - (line1Y + t * yDiff);
592  }
593 
594  return xDiff * xDiff + yDiff * yDiff;
595  }
596 
597  public static Vector2 GetClosestPointOnLineSegment(Vector2 lineA, Vector2 lineB, Vector2 point)
598  {
599  float xDiff = lineB.X - lineA.X;
600  float yDiff = lineB.Y - lineA.Y;
601 
602  if (xDiff == 0 && yDiff == 0)
603  {
604  return lineA;
605  }
606 
607  // Calculate the t that minimizes the distance.
608  float t = ((point.X - lineA.X) * xDiff + (point.Y - lineA.Y) * yDiff) / (xDiff * xDiff + yDiff * yDiff);
609 
610  // See if this represents one of the segment's
611  // end points or a point in the middle.
612  if (t < 0)
613  {
614  return lineA;
615  }
616  else if (t > 1)
617  {
618  return lineB;
619  }
620  else
621  {
622  return new Vector2(lineA.X + t * xDiff, lineA.Y + t * yDiff);
623  }
624  }
625 
626  public static bool CircleIntersectsRectangle(Vector2 circlePos, float radius, Rectangle rect)
627  {
628  int halfWidth = rect.Width / 2;
629  float xDist = Math.Abs(circlePos.X - (rect.X + halfWidth));
630  if (xDist > halfWidth + radius) { return false; }
631 
632  int halfHeight = rect.Height / 2;
633  float yDist = Math.Abs(circlePos.Y - (rect.Y + halfHeight));
634  if (yDist > halfHeight + radius) { return false; }
635 
636  if (xDist <= halfWidth || yDist <= halfHeight) { return true; }
637 
638  float distSqX = xDist - halfWidth;
639  float distSqY = yDist - halfHeight;
640 
641  return distSqX * distSqX + distSqY * distSqY <= radius * radius;
642  }
643 
651  public static Vector2 GetPointOnCircumference(Vector2 center, float radius, float angle)
652  {
653  return new Vector2(
654  center.X + radius * (float)Math.Cos(angle),
655  center.Y + radius * (float)Math.Sin(angle));
656  }
657 
666  public static Vector2[] GetPointsOnCircumference(Vector2 center, float radius, int points, float firstAngle = 0.0f)
667  {
668  var maxAngle = (float)(2 * Math.PI);
669  var angleStep = maxAngle / points;
670  var coordinates = new Vector2[points];
671  for (int i = 0; i < points; i++)
672  {
673  var angle = firstAngle + (i * angleStep);
674  if (angle > maxAngle) { angle -= maxAngle; }
675  coordinates[i] = GetPointOnCircumference(center, radius, angle);
676  }
677  return coordinates;
678  }
679 
684  public static List<Vector2[]> TriangulateConvexHull(List<Vector2> vertices, Vector2 center)
685  {
686  List<Vector2[]> triangles = new List<Vector2[]>();
687  vertices.Sort(new CompareCCW(center));
688  for (int i = 0; i < vertices.Count; i++)
689  {
690  triangles.Add(new Vector2[3] { center, vertices[i], vertices[(i + 1) % vertices.Count] });
691  }
692  return triangles;
693  }
694 
695  public static List<Vector2> GiftWrap(List<Vector2> points)
696  {
697  if (points.Count == 0) return points;
698 
699  Vector2 leftMost = points[0];
700  foreach (Vector2 point in points)
701  {
702  if (point.X < leftMost.X) leftMost = point;
703  }
704 
705  List<Vector2> wrappedPoints = new List<Vector2>();
706 
707  Vector2 currPoint = leftMost;
708  Vector2 endPoint;
709  do
710  {
711  wrappedPoints.Add(currPoint);
712  endPoint = points[0];
713 
714  for (int i = 1; i < points.Count; i++)
715  {
716  if (points[i].NearlyEquals(currPoint)) continue;
717  if (currPoint == endPoint ||
718  MathUtils.VectorOrientation(currPoint, endPoint, points[i]) == -1)
719  {
720  endPoint = points[i];
721  }
722  }
723 
724  currPoint = endPoint;
725 
726  }
727  while (endPoint != leftMost);
728 
729  return wrappedPoints;
730  }
731 
732  public static List<Vector2[]> GenerateJaggedLine(Vector2 start, Vector2 end, int iterations, float offsetAmount, Rectangle? bounds = null)
733  {
734  List<Vector2[]> segments = new List<Vector2[]>
735  {
736  new Vector2[] { start, end }
737  };
738 
739  for (int n = 0; n < iterations; n++)
740  {
741  for (int i = 0; i < segments.Count; i++)
742  {
743  Vector2 startSegment = segments[i][0];
744  Vector2 endSegment = segments[i][1];
745 
746  segments.RemoveAt(i);
747 
748  Vector2 midPoint = (startSegment + endSegment) / 2.0f;
749 
750  Vector2 normal = Vector2.Normalize(endSegment - startSegment);
751  normal = new Vector2(-normal.Y, normal.X);
752  midPoint += normal * Rand.Range(-offsetAmount, offsetAmount, Rand.RandSync.ServerAndClient);
753 
754  if (bounds.HasValue)
755  {
756  if (midPoint.X < bounds.Value.X)
757  {
758  midPoint.X = bounds.Value.X + (bounds.Value.X - midPoint.X);
759  }
760  else if (midPoint.X > bounds.Value.Right)
761  {
762  midPoint.X = bounds.Value.Right - (midPoint.X - bounds.Value.Right);
763  }
764  if (midPoint.Y < bounds.Value.Y)
765  {
766  midPoint.Y = bounds.Value.Y + (bounds.Value.Y - midPoint.Y);
767  }
768  else if (midPoint.Y > bounds.Value.Bottom)
769  {
770  midPoint.Y = bounds.Value.Bottom - (midPoint.Y - bounds.Value.Bottom);
771  }
772  }
773 
774  segments.Insert(i, new Vector2[] { startSegment, midPoint });
775  segments.Insert(i + 1, new Vector2[] { midPoint, endSegment });
776 
777  i++;
778  }
779 
780  offsetAmount *= 0.5f;
781  }
782 
783  return segments;
784  }
785 
786  // Returns the human-readable file size for an arbitrary, 64-bit file size
787  // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB"
788  public static string GetBytesReadable(long i)
789  {
790  // Get absolute value
791  long absolute_i = (i < 0 ? -i : i);
792  // Determine the suffix and readable value
793  string suffix;
794  double readable;
795  if (absolute_i >= 0x1000000000000000) // Exabyte
796  {
797  suffix = "EB";
798  readable = (i >> 50);
799  }
800  else if (absolute_i >= 0x4000000000000) // Petabyte
801  {
802  suffix = "PB";
803  readable = (i >> 40);
804  }
805  else if (absolute_i >= 0x10000000000) // Terabyte
806  {
807  suffix = "TB";
808  readable = (i >> 30);
809  }
810  else if (absolute_i >= 0x40000000) // Gigabyte
811  {
812  suffix = "GB";
813  readable = (i >> 20);
814  }
815  else if (absolute_i >= 0x100000) // Megabyte
816  {
817  suffix = "MB";
818  readable = (i >> 10);
819  }
820  else if (absolute_i >= 0x400) // Kilobyte
821  {
822  suffix = "KB";
823  readable = i;
824  }
825  else
826  {
827  return i.ToString("0 B"); // Byte
828  }
829  // Divide by 1024 to get fractional value
830  readable = (readable / 1024);
831  // Return formatted number with suffix
832  return readable.ToString("0.# ") + suffix;
833  }
834 
835  public static void SplitRectanglesHorizontal(List<Rectangle> rects, Vector2 point)
836  {
837  for (int i = 0; i < rects.Count; i++)
838  {
839  if (point.Y > rects[i].Y && point.Y < rects[i].Y + rects[i].Height)
840  {
841  Rectangle rect1 = rects[i];
842  Rectangle rect2 = rects[i];
843 
844  rect1.Height = (int)(point.Y - rects[i].Y);
845 
846  rect2.Height = rects[i].Height - rect1.Height;
847  rect2.Y = rect1.Y + rect1.Height;
848  rects[i] = rect1;
849  rects.Insert(i + 1, rect2); i++;
850  }
851  }
852  }
853 
854  public static void SplitRectanglesVertical(List<Rectangle> rects, Vector2 point)
855  {
856  for (int i = 0; i < rects.Count; i++)
857  {
858  if (point.X>rects[i].X && point.X<rects[i].X+rects[i].Width)
859  {
860  Rectangle rect1 = rects[i];
861  Rectangle rect2 = rects[i];
862 
863  rect1.Width = (int)(point.X-rects[i].X);
864 
865  rect2.Width = rects[i].Width - rect1.Width;
866  rect2.X = rect1.X + rect1.Width;
867  rects[i] = rect1;
868  rects.Insert(i + 1, rect2); i++;
869  }
870  }
871 
872  /*for (int i = 0; i < rects.Count; i++)
873  {
874  if (rects[i].Width <= 0 || rects[i].Height <= 0)
875  {
876  rects.RemoveAt(i); i--;
877  }
878  }*/
879  }
880 
884  // References:
885  // http://floating-point-gui.de/errors/comparison/
886  // https://stackoverflow.com/questions/3874627/floating-point-comparison-functions-for-c-sharp
887  public static bool NearlyEqual(float a, float b, float epsilon = 0.0001f)
888  {
889  if (a == b)
890  {
891  //shortcut, handles infinities
892  return true;
893  }
894 
895  if (a == 0 || b == 0)
896  {
897  //if a or b is zero, relative error is less meaningful
898  return Math.Abs(a - b) < epsilon;
899  }
900 
901  float absA = Math.Abs(a);
902  float absB = Math.Abs(b);
903  float absAB = absA + absB;
904  if (absAB < epsilon)
905  {
906  // a and b extremely close to zero, relative error is less meaningful
907  return true;
908  }
909 
910  float diff = Math.Abs(a - b);
911  // use relative error
912  return diff / absAB < epsilon;
913  }
914 
918  public static bool NearlyEqual(Vector2 a, Vector2 b, float epsilon = 0.0001f)
919  {
920  return NearlyEqual(a.X, b.X, epsilon) && NearlyEqual(a.Y, b.Y, epsilon);
921  }
922 
926  public static Vector2 Bezier(Vector2 start, Vector2 control, Vector2 end, float t)
927  {
928  return Pow(1 - t, 2) * start + 2 * t * (1 - t) * control + Pow(t, 2) * end;
929  }
930 
931  public static float Pow(float f, float p)
932  {
933  return (float)Math.Pow(f, p);
934  }
935 
936  public static float Pow2(float f) => f * f;
937 
941  public static Vector2 ToVector2(this Alignment alignment)
942  {
943  Vector2 vector = new Vector2(0.0f,0.0f);
944  if (alignment.HasFlag(Alignment.Left))
945  {
946  vector.X = -1.0f;
947  }
948  else if (alignment.HasFlag(Alignment.Right))
949  {
950  vector.X = 1.0f;
951  }
952  if (alignment.HasFlag(Alignment.Top))
953  {
954  vector.Y = -1.0f;
955  }
956  else if (alignment.HasFlag(Alignment.Bottom))
957  {
958  vector.Y = 1.0f;
959  }
960  return vector;
961  }
962 
968  public static Vector2 RotatePointAroundTarget(Vector2 point, Vector2 target, float radians, bool clockWise = true)
969  {
970  var sin = Math.Sin(radians);
971  var cos = Math.Cos(radians);
972  if (!clockWise)
973  {
974  sin = -sin;
975  }
976  Vector2 dir = point - target;
977  var x = (cos * dir.X) - (sin * dir.Y) + target.X;
978  var y = (sin * dir.X) + (cos * dir.Y) + target.Y;
979  return new Vector2((float)x, (float)y);
980  }
981 
985  public static Vector2 RotatePoint(Vector2 point, float radians)
986  {
987  var sin = Math.Sin(radians);
988  var cos = Math.Cos(radians);
989  var x = (cos * point.X) - (sin * point.Y);
990  var y = (sin * point.X) + (cos * point.Y);
991  return new Vector2((float)x, (float)y);
992  }
993 
998  public static Vector2[] GetImaginaryRect(Vector2 up, Vector2 center, Vector2 size)
999  {
1000  return GetImaginaryRect(new Vector2[4], up, center, size);
1001  }
1002 
1007  public static Vector2[] GetImaginaryRect(Vector2[] corners, Vector2 up, Vector2 center, Vector2 size)
1008  {
1009  if (corners.Length != 4)
1010  {
1011  throw new Exception("Invalid length for the corners array. Must be 4.");
1012  }
1013  Vector2 halfSize = size / 2;
1014  Vector2 left = up.Right();
1015  corners[0] = center + up * halfSize.Y + left * halfSize.X;
1016  corners[1] = center + up * halfSize.Y - left * halfSize.X;
1017  corners[2] = center - up * halfSize.Y - left * halfSize.X;
1018  corners[3] = center - up * halfSize.Y + left * halfSize.X;
1019  return corners;
1020  }
1021 
1027  public static bool RectangleContainsPoint(Vector2[] corners, Vector2 point)
1028  {
1029  if (corners.Length != 4)
1030  {
1031  throw new Exception("Invalid length of the corners array! Must be 4");
1032  }
1033  return RectangleContainsPoint(corners[0], corners[1], corners[2], corners[3], point);
1034  }
1035 
1041  public static bool RectangleContainsPoint(Vector2 c1, Vector2 c2, Vector2 c3, Vector2 c4, Vector2 point)
1042  {
1043  return TriangleContainsPoint(c1, c2, c3, point) || TriangleContainsPoint(c1, c3, c4, point);
1044  }
1045 
1049  public static bool TriangleContainsPoint(Vector2 c1, Vector2 c2, Vector2 c3, Vector2 point)
1050  {
1051  // Compute vectors
1052  Vector2 v0 = c3 - c1;
1053  Vector2 v1 = c2 - c1;
1054  Vector2 v2 = point - c1;
1055 
1056  // Compute dot products
1057  float dot00 = Vector2.Dot(v0, v0);
1058  float dot01 = Vector2.Dot(v0, v1);
1059  float dot02 = Vector2.Dot(v0, v2);
1060  float dot11 = Vector2.Dot(v1, v1);
1061  float dot12 = Vector2.Dot(v1, v2);
1062 
1063  // Compute barycentric coordinates
1064  float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
1065  float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
1066  float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
1067 
1068  // Check if the point is in triangle
1069  return u >= 0 && v >= 0 && (u + v) < 1;
1070  }
1071 
1075  public static float InverseLerp(float min, float max, float v)
1076  {
1077  float diff = max - min;
1078  // Ensure that we don't get division by zero exceptions.
1079  if (diff == 0) { return v >= max ? 1f : 0f; }
1080  return MathHelper.Clamp((v - min) / diff, 0f, 1f);
1081  }
1082 
1083  public static float Min(params float[] vals)
1084  {
1085  return vals.Min();
1086  }
1087 
1088  public static float Max(params float[] vals)
1089  {
1090  return vals.Max();
1091  }
1092  }
1093 
1094  class CompareCW : IComparer<Vector2>
1095  {
1096  private Vector2 center;
1097 
1098  public CompareCW(Vector2 center)
1099  {
1100  this.center = center;
1101  }
1102  public int Compare(Vector2 a, Vector2 b)
1103  {
1104  return Compare(a, b, center);
1105  }
1106 
1107  public static int Compare(Vector2 a, Vector2 b, Vector2 center)
1108  {
1109  if (a == b) { return 0; }
1110  if (a.X - center.X >= 0 && b.X - center.X < 0) { return 1; }
1111  if (a.X - center.X < 0 && b.X - center.X >= 0) { return -1; }
1112  if (a.X - center.X == 0 && b.X - center.X == 0)
1113  {
1114  if (a.Y - center.Y >= 0 || b.Y - center.Y >= 0) { return Math.Sign(a.Y - b.Y); }
1115  return Math.Sign(a.Y - b.Y);
1116  }
1117 
1118  // compute the cross product of vectors (center -> a) x (center -> b)
1119  float det = (a.X - center.X) * (b.Y - center.Y) - (b.X - center.X) * (a.Y - center.Y);
1120  if (det < 0) { return 1; }
1121  if (det > 0) { return -1; }
1122 
1123  // points a and b are on the same line from the center
1124  // check which point is closer to the center
1125  float d1 = (a.X - center.X) * (a.X - center.X) + (a.Y - center.Y) * (a.Y - center.Y);
1126  float d2 = (b.X - center.X) * (b.X - center.X) + (b.Y - center.Y) * (b.Y - center.Y);
1127  return Math.Sign(d1 - d2);
1128  }
1129  }
1130 
1131  class CompareCCW : IComparer<Vector2>
1132  {
1133  private Vector2 center;
1134 
1135  public CompareCCW(Vector2 center)
1136  {
1137  this.center = center;
1138  }
1139  public int Compare(Vector2 a, Vector2 b)
1140  {
1141  return -CompareCW.Compare(a, b, center);
1142  }
1143  public static int Compare(Vector2 a, Vector2 b, Vector2 center)
1144  {
1145  return -CompareCW.Compare(a, b, center);
1146  }
1147  }
1148 }
static int Compare(Vector2 a, Vector2 b, Vector2 center)
static int Compare(Vector2 a, Vector2 b, Vector2 center)