Client LuaCsForBarotrauma
Quad2D.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using Microsoft.Xna.Framework;
5 
6 namespace Barotrauma;
7 
8 readonly record struct Quad2D(Vector2 A, Vector2 B, Vector2 C, Vector2 D)
9 {
10  public Vector2 Centroid => (A + B + C + D) / 4;
11 
12  public static Quad2D FromRectangle(RectangleF rectangle)
13  {
14  return new Quad2D(
15  A: (rectangle.Left, rectangle.Top),
16  B: (rectangle.Right, rectangle.Top),
17  C: (rectangle.Right, rectangle.Bottom),
18  D: (rectangle.Left, rectangle.Bottom));
19  }
20 
21  public static Quad2D FromSubmarineRectangle(RectangleF rectangle)
22  {
23  return new Quad2D(
24  A: (rectangle.X, rectangle.Y),
25  B: (rectangle.X + rectangle.Width, rectangle.Y),
26  C: (rectangle.X + rectangle.Width, rectangle.Y - rectangle.Height),
27  D: (rectangle.X, rectangle.Y - rectangle.Height));
28  }
29 
30  public Quad2D Rotated(float radians)
31  {
32  return new Quad2D(
33  A: MathUtils.RotatePointAroundTarget(point: A, target: Centroid, radians: radians),
34  B: MathUtils.RotatePointAroundTarget(point: B, target: Centroid, radians: radians),
35  C: MathUtils.RotatePointAroundTarget(point: C, target: Centroid, radians: radians),
36  D: MathUtils.RotatePointAroundTarget(point: D, target: Centroid, radians: radians));
37  }
38 
39  public RectangleF BoundingAxisAlignedRectangle
40  {
41  get
42  {
43  Vector2 min = (
44  X: Math.Min(A.X, Math.Min(B.X, Math.Min(C.X, D.X))),
45  Y: Math.Min(A.Y, Math.Min(B.Y, Math.Min(C.Y, D.Y))));
46  Vector2 max = (
47  X: Math.Max(A.X, Math.Max(B.X, Math.Max(C.X, D.X))),
48  Y: Math.Max(A.Y, Math.Max(B.Y, Math.Max(C.Y, D.Y))));
49  return new RectangleF(location: min, size: max - min);
50  }
51  }
52 
53  public bool TryGetEdges(Span<(Vector2 A, Vector2 B)> outputSpan)
54  {
55  if (outputSpan.Length < 4) { return false; }
56 
57  outputSpan[0] = (A, B);
58  outputSpan[1] = (B, C);
59  outputSpan[2] = (C, D);
60  outputSpan[3] = (D, A);
61  return true;
62  }
63 
64  public bool Contains(Vector2 point)
65  {
66  // Break up the quad into two triangles and then see if the point is in either triangle.
67  // Since quads can be concave, care needs to be taken when splitting in two.
68 
69  (Triangle2D triangle1, Triangle2D triangle2)
70  = (new Triangle2D(A, B, C), new Triangle2D(A, D, C));
71 
72  // If D is inside of the triangle ABC, or B is inside of the triangle ADC,
73  // then the quad is concave and we split at the wrong diagonal.
74  // Splitting at the other diagonal should be fine.
75  if (triangle1.Contains(D) || triangle2.Contains(B))
76  {
77  (triangle1, triangle2) = (new Triangle2D(B, C, D), new Triangle2D(B, A, D));
78  }
79 
80  return triangle1.Contains(point) || triangle2.Contains(point);
81  }
82 
83  public bool Intersects(Quad2D other)
84  {
85  if (!BoundingAxisAlignedRectangle.Intersects(other.BoundingAxisAlignedRectangle))
86  {
87  return false;
88  }
89 
90  if (Contains(other.A)) { return true; }
91  if (Contains(other.B)) { return true; }
92  if (Contains(other.C)) { return true; }
93  if (Contains(other.D)) { return true; }
94 
95  if (other.Contains(A)) { return true; }
96  if (other.Contains(B)) { return true; }
97  if (other.Contains(C)) { return true; }
98  if (other.Contains(D)) { return true; }
99 
100  Span<(Vector2 A, Vector2 B)> myEdges = stackalloc (Vector2 A, Vector2 B)[4];
101  TryGetEdges(myEdges);
102  Span<(Vector2 A, Vector2 B)> otherEdges = stackalloc (Vector2 A, Vector2 B)[4];
103  other.TryGetEdges(otherEdges);
104  foreach (var edge in myEdges)
105  {
106  foreach (var otherEdge in otherEdges)
107  {
108  if (MathUtils.LineSegmentsIntersect(edge.A, edge.B, otherEdge.A, otherEdge.B)) { return true; }
109  }
110  }
111  return false;
112  }
113 }