Client LuaCsForBarotrauma
ShapeExtensions.cs
1 using System;
2 using System.Collections.Generic;
3 using Microsoft.Xna.Framework;
4 using Microsoft.Xna.Framework.Graphics;
5 using System.Linq;
6 
7 namespace Barotrauma
8 {
13  public static class ShapeExtensions
14  {
15  private static Texture2D _whitePixelTexture;
16 
17  private static Texture2D GetTexture(SpriteBatch spriteBatch)
18  {
19  if (_whitePixelTexture == null)
20  {
21  CrossThread.RequestExecutionOnMainThread(() =>
22  {
23  _whitePixelTexture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
24  _whitePixelTexture.SetData(new[] { Color.White });
25  });
26  }
27 
28  return _whitePixelTexture;
29  }
30 
34  public static void DrawPolygon(this SpriteBatch spriteBatch, Vector2 position, Polygon polygon, Color color,
35  float thickness = 1f)
36  {
37  DrawPolygon(spriteBatch, position, polygon.Vertices, color, thickness);
38  }
39 
43  public static void DrawPolygon(this SpriteBatch spriteBatch, Vector2 offset, IReadOnlyList<Vector2> points, Color color,
44  float thickness = 1f)
45  {
46  if (points.Count == 0)
47  return;
48 
49  if (points.Count == 1)
50  {
51  DrawPoint(spriteBatch, points[0], color, (int)thickness);
52  return;
53  }
54 
55  var texture = GetTexture(spriteBatch);
56 
57  for (var i = 0; i < points.Count - 1; i++)
58  DrawPolygonEdge(spriteBatch, points[i] + offset, points[i + 1] + offset, color, thickness);
59 
60  DrawPolygonEdge(spriteBatch, points[points.Count - 1] + offset, points[0] + offset, color,
61  thickness);
62  }
63 
67  public static void DrawPolygonInner(this SpriteBatch spriteBatch, Vector2 offset, IReadOnlyList<Vector2> points, Color color, float thickness = 1f)
68  {
69  if (points.Count == 0) { return; }
70 
71  if (points.Count == 1)
72  {
73  DrawPoint(spriteBatch, points[0], color, (int)thickness);
74  return;
75  }
76 
77  for (var i = 0; i < points.Count - 1; i++)
78  {
79  Vector2 point1 = points[i] + offset,
80  point2 = points[i + 1] + offset;
81 
82  DrawPolygonEdgeInner(spriteBatch, point1, point2, color, thickness);
83  }
84 
85  DrawPolygonEdgeInner(spriteBatch, points[^1] + offset, points[0] + offset, color, thickness);
86  }
87 
88  private static void DrawPolygonEdgeInner(SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, float thickness)
89  {
90  var length = Vector2.Distance(point1, point2) + thickness;
91  var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
92  var scale = new Vector2(length, thickness);
93  Vector2 middle = new Vector2((point1.X + point2.X) / 2f, (point1.Y + point2.Y) / 2f);
94  Texture2D tex = GUI.WhiteTexture;
95  spriteBatch.Draw(tex, middle, null, color, angle, new Vector2(tex.Width / 2f, tex.Height / 2f), scale, SpriteEffects.None, 0);
96  }
97 
98  private static void DrawPolygonEdge(SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, float thickness)
99  {
100  var length = Vector2.Distance(point1, point2);
101  var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
102  var scale = new Vector2(length, thickness);
103  spriteBatch.Draw(GetTexture(spriteBatch), point1, null, color, angle, Vector2.Zero, scale, SpriteEffects.None, 0);
104  }
105 
109  public static void DrawLine(this SpriteBatch spriteBatch, float x1, float y1, float x2, float y2, Color color,
110  float thickness = 1f)
111  {
112  DrawLine(spriteBatch, new Vector2(x1, y1), new Vector2(x2, y2), color, thickness);
113  }
114 
115  public static void DrawLineWithTexture(this SpriteBatch spriteBatch, Texture2D tex, Vector2 point1, Vector2 point2,
116  Color color, float thickness = 1f)
117  {
118  // calculate the distance between the two vectors
119  var distance = Vector2.Distance(point1, point2);
120 
121  // calculate the angle between the two vectors
122  var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
123 
124  DrawLine(spriteBatch, tex, point1, distance, angle, color, thickness);
125  }
126 
130  public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color,
131  float thickness = 1f)
132  {
133  // calculate the distance between the two vectors
134  var distance = Vector2.Distance(point1, point2);
135 
136  // calculate the angle between the two vectors
137  var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
138 
139  DrawLine(spriteBatch, GetTexture(spriteBatch), point1, distance, angle, color, thickness);
140  }
141 
145  public static void DrawLine(this SpriteBatch spriteBatch, Texture2D tex, Vector2 point, float length, float angle, Color color,
146  float thickness = 1f)
147  {
148  var origin = new Vector2(0f, tex.Height / 2f);
149  var scale = new Vector2(length / tex.Width, thickness / tex.Height);
150  spriteBatch.Draw(tex, point, null, color, angle, origin, scale, SpriteEffects.None, 0);
151  }
152 
156  public static void DrawPoint(this SpriteBatch spriteBatch, float x, float y, Color color, float size = 1f)
157  {
158  DrawPoint(spriteBatch, new Vector2(x, y), color, size);
159  }
160 
164  public static void DrawPoint(this SpriteBatch spriteBatch, Vector2 position, Color color, float size = 1f)
165  {
166  var offset = new Vector2(0.5f) - new Vector2(size * 0.5f);
167  spriteBatch.Draw(GetTexture(spriteBatch), position + offset, null, color, 0.0f, Vector2.Zero, new Vector2(size), SpriteEffects.None, 0);
168  }
169 
170  public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color,
171  float thickness = 1f)
172  {
173  DrawPolygon(spriteBatch, center, CreateCircle(radius, sides), color, thickness);
174  }
175 
176  public static void DrawCircle(this SpriteBatch spriteBatch, float x, float y, float radius, int sides,
177  Color color, float thickness = 1f)
178  {
179  DrawPolygon(spriteBatch, new Vector2(x, y), CreateCircle(radius, sides), color, thickness);
180  }
181 
182  public static void DrawSector(this SpriteBatch spriteBatch, Vector2 center, float radius, float radians, int sides, Color color, float offset = 0, float thickness = 1)
183  {
184  DrawPolygon(spriteBatch, center, CreateSector(radius, sides, radians, offset), color, thickness);
185  }
186 
187  private static Vector2[] CreateSector(double radius, int sides, float radians, float offset = 0)
188  {
189  //circle sectors need one extra point at the center
190  var points = new Vector2[radians < MathHelper.TwoPi ? sides + 1 : sides];
191  var step = radians / sides;
192 
193  double theta = offset;
194  for (var i = 0; i < sides; i++)
195  {
196  points[i] = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * (float)radius;
197  theta += step;
198  }
199 
200  return points;
201  }
202 
203  private static Vector2[] CreateCircle(double radius, int sides)
204  {
205  return CreateSector(radius, sides, MathHelper.TwoPi);
206  }
207  }
208 
212  public class Polygon : IEquatable<Polygon>
213  {
214  public Polygon(IEnumerable<Vector2> vertices)
215  {
216  _localVertices = vertices.ToArray();
217  _transformedVertices = _localVertices;
218  _offset = Vector2.Zero;
219  _rotation = 0;
220  _scale = Vector2.One;
221  _isDirty = false;
222  }
223 
224  private readonly Vector2[] _localVertices;
225  private Vector2[] _transformedVertices;
226  private Vector2 _offset;
227  private float _rotation;
228  private Vector2 _scale;
229  private bool _isDirty;
230 
231  public Vector2[] Vertices
232  {
233  get
234  {
235  if (_isDirty)
236  {
237  _transformedVertices = GetTransformedVertices();
238  _isDirty = false;
239  }
240 
241  return _transformedVertices;
242  }
243  }
244 
245  public float Left
246  {
247  get { return Vertices.Min(v => v.X); }
248  }
249 
250  public float Right
251  {
252  get { return Vertices.Max(v => v.X); }
253  }
254 
255  public float Top
256  {
257  get { return Vertices.Min(v => v.Y); }
258  }
259 
260  public float Bottom
261  {
262  get { return Vertices.Max(v => v.Y); }
263  }
264 
265  public void Offset(Vector2 amount)
266  {
267  _offset += amount;
268  _isDirty = true;
269  }
270 
271  public void Rotate(float amount)
272  {
273  _rotation += amount;
274  _isDirty = true;
275  }
276 
277  public void Scale(Vector2 amount)
278  {
279  _scale += amount;
280  _isDirty = true;
281  }
282 
283  private Vector2[] GetTransformedVertices()
284  {
285  var newVertices = new Vector2[_localVertices.Length];
286  var isScaled = _scale != Vector2.One;
287 
288  for (var i = 0; i < _localVertices.Length; i++)
289  {
290  var p = _localVertices[i];
291 
292  if (isScaled)
293  p *= _scale;
294 
295  // ReSharper disable once CompareOfFloatsByEqualityOperator
296  if (_rotation != 0)
297  {
298  var cos = (float)Math.Cos(_rotation);
299  var sin = (float)Math.Sin(_rotation);
300  p = new Vector2(cos * p.X - sin * p.Y, sin * p.X + cos * p.Y);
301  }
302 
303  newVertices[i] = p + _offset;
304  }
305 
306  return newVertices;
307  }
308 
309  public Polygon TransformedCopy(Vector2 offset, float rotation, Vector2 scale)
310  {
311  var polygon = new Polygon(_localVertices);
312  polygon.Offset(offset);
313  polygon.Rotate(rotation);
314  polygon.Scale(scale - Vector2.One);
315  return new Polygon(polygon.Vertices);
316  }
317 
318  public bool Contains(Vector2 point)
319  {
320  return Contains(point.X, point.Y);
321  }
322 
323  public bool Contains(float x, float y)
324  {
325  var intersects = 0;
326  var vertices = Vertices;
327 
328  for (var i = 0; i < vertices.Length; i++)
329  {
330  var x1 = vertices[i].X;
331  var y1 = vertices[i].Y;
332  var x2 = vertices[(i + 1) % vertices.Length].X;
333  var y2 = vertices[(i + 1) % vertices.Length].Y;
334 
335  if ((((y1 <= y) && (y < y2)) || ((y2 <= y) && (y < y1))) && (x < (x2 - x1) / (y2 - y1) * (y - y1) + x1))
336  intersects++;
337  }
338 
339  return (intersects & 1) == 1;
340  }
341 
342  public static bool operator ==(Polygon a, Polygon b)
343  {
344  return a.Equals(b);
345  }
346 
347  public static bool operator !=(Polygon a, Polygon b)
348  {
349  return !(a == b);
350  }
351 
352  public override bool Equals(object obj)
353  {
354  if (ReferenceEquals(null, obj)) return false;
355  return obj is Polygon && Equals((Polygon)obj);
356  }
357 
358  public bool Equals(Polygon other)
359  {
360  return Vertices.SequenceEqual(other.Vertices);
361  }
362 
363  public override int GetHashCode()
364  {
365  unchecked
366  {
367  return Vertices.Aggregate(27, (current, v) => current + 13 * current + v.GetHashCode());
368  }
369  }
370  }
371 }
Original source: https://github.com/craftworkgames/MonoGame.Extended/blob/develop/Source/MonoGame....
bool Equals(Polygon other)
void Scale(Vector2 amount)
void Offset(Vector2 amount)
static bool operator!=(Polygon a, Polygon b)
Polygon(IEnumerable< Vector2 > vertices)
static bool operator==(Polygon a, Polygon b)
void Rotate(float amount)
override int GetHashCode()
override bool Equals(object obj)
bool Contains(Vector2 point)
bool Contains(float x, float y)
Polygon TransformedCopy(Vector2 offset, float rotation, Vector2 scale)