A game framework written with osu! in mind.
at master 140 lines 6.0 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using System.Runtime.CompilerServices; 6using System.Runtime.InteropServices; 7using osuTK; 8using osu.Framework.Utils; 9 10namespace osu.Framework.Graphics.Primitives 11{ 12 [StructLayout(LayoutKind.Sequential)] 13 public readonly struct Quad : IConvexPolygon, IEquatable<Quad> 14 { 15 // Note: Do not change the order of vertices. They are ordered in screen-space counter-clockwise fashion. 16 // See: IPolygon.GetVertices() 17 public readonly Vector2 TopLeft; 18 public readonly Vector2 BottomLeft; 19 public readonly Vector2 BottomRight; 20 public readonly Vector2 TopRight; 21 22 public Quad(Vector2 topLeft, Vector2 topRight, Vector2 bottomLeft, Vector2 bottomRight) 23 { 24 TopLeft = topLeft; 25 TopRight = topRight; 26 BottomLeft = bottomLeft; 27 BottomRight = bottomRight; 28 } 29 30 public Quad(float x, float y, float width, float height) 31 : this() 32 { 33 TopLeft = new Vector2(x, y); 34 TopRight = new Vector2(x + width, y); 35 BottomLeft = new Vector2(x, y + height); 36 BottomRight = new Vector2(x + width, y + height); 37 } 38 39 public static implicit operator Quad(RectangleI r) => FromRectangle(r); 40 public static implicit operator Quad(RectangleF r) => FromRectangle(r); 41 42 public static Quad FromRectangle(RectangleF rectangle) => 43 new Quad(new Vector2(rectangle.Left, rectangle.Top), 44 new Vector2(rectangle.Right, rectangle.Top), 45 new Vector2(rectangle.Left, rectangle.Bottom), 46 new Vector2(rectangle.Right, rectangle.Bottom)); 47 48 public static Quad operator *(Quad r, Matrix3 m) => 49 new Quad( 50 Vector2Extensions.Transform(r.TopLeft, m), 51 Vector2Extensions.Transform(r.TopRight, m), 52 Vector2Extensions.Transform(r.BottomLeft, m), 53 Vector2Extensions.Transform(r.BottomRight, m)); 54 55 public Matrix2 BasisTransform 56 { 57 get 58 { 59 Vector2 row0 = TopRight - TopLeft; 60 Vector2 row1 = BottomLeft - TopLeft; 61 62 if (row0 != Vector2.Zero) 63 row0 /= row0.LengthSquared; 64 65 if (row1 != Vector2.Zero) 66 row1 /= row1.LengthSquared; 67 68 return new Matrix2( 69 row0.X, row0.Y, 70 row1.X, row1.Y); 71 } 72 } 73 74 public Vector2 Centre => (TopLeft + TopRight + BottomLeft + BottomRight) / 4; 75 public Vector2 Size => new Vector2(Width, Height); 76 77 public float Width => Vector2Extensions.Distance(TopLeft, TopRight); 78 public float Height => Vector2Extensions.Distance(TopLeft, BottomLeft); 79 80 public RectangleI AABB 81 { 82 get 83 { 84 int xMin = (int)Math.Floor(Math.Min(TopLeft.X, Math.Min(TopRight.X, Math.Min(BottomLeft.X, BottomRight.X)))); 85 int yMin = (int)Math.Floor(Math.Min(TopLeft.Y, Math.Min(TopRight.Y, Math.Min(BottomLeft.Y, BottomRight.Y)))); 86 int xMax = (int)Math.Ceiling(Math.Max(TopLeft.X, Math.Max(TopRight.X, Math.Max(BottomLeft.X, BottomRight.X)))); 87 int yMax = (int)Math.Ceiling(Math.Max(TopLeft.Y, Math.Max(TopRight.Y, Math.Max(BottomLeft.Y, BottomRight.Y)))); 88 89 return new RectangleI(xMin, yMin, xMax - xMin, yMax - yMin); 90 } 91 } 92 93 public RectangleF AABBFloat 94 { 95 get 96 { 97 float xMin = Math.Min(TopLeft.X, Math.Min(TopRight.X, Math.Min(BottomLeft.X, BottomRight.X))); 98 float yMin = Math.Min(TopLeft.Y, Math.Min(TopRight.Y, Math.Min(BottomLeft.Y, BottomRight.Y))); 99 float xMax = Math.Max(TopLeft.X, Math.Max(TopRight.X, Math.Max(BottomLeft.X, BottomRight.X))); 100 float yMax = Math.Max(TopLeft.Y, Math.Max(TopRight.Y, Math.Max(BottomLeft.Y, BottomRight.Y))); 101 102 return new RectangleF(xMin, yMin, xMax - xMin, yMax - yMin); 103 } 104 } 105 106 public ReadOnlySpan<Vector2> GetAxisVertices() => GetVertices(); 107 108 public ReadOnlySpan<Vector2> GetVertices() => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in TopLeft), 4); 109 110 public bool Contains(Vector2 pos) => 111 new Triangle(BottomRight, BottomLeft, TopRight).Contains(pos) || 112 new Triangle(TopLeft, TopRight, BottomLeft).Contains(pos); 113 114 /// <summary> 115 /// Computes the area of this <see cref="Quad"/>. 116 /// </summary> 117 /// <remarks> 118 /// If the quad is self-intersecting the area is interpreted as the sum of all positive and negative areas and not the "visible area" enclosed by the <see cref="Quad"/>. 119 /// </remarks> 120 public float Area => 0.5f * Math.Abs(Vector2Extensions.GetOrientation(GetVertices())); 121 122 public bool Equals(Quad other) => 123 TopLeft == other.TopLeft && 124 TopRight == other.TopRight && 125 BottomLeft == other.BottomLeft && 126 BottomRight == other.BottomRight; 127 128 public bool AlmostEquals(Quad other) => 129 Precision.AlmostEquals(TopLeft.X, other.TopLeft.X) && 130 Precision.AlmostEquals(TopLeft.Y, other.TopLeft.Y) && 131 Precision.AlmostEquals(TopRight.X, other.TopRight.X) && 132 Precision.AlmostEquals(TopRight.Y, other.TopRight.Y) && 133 Precision.AlmostEquals(BottomLeft.X, other.BottomLeft.X) && 134 Precision.AlmostEquals(BottomLeft.Y, other.BottomLeft.Y) && 135 Precision.AlmostEquals(BottomRight.X, other.BottomRight.X) && 136 Precision.AlmostEquals(BottomRight.Y, other.BottomRight.Y); 137 138 public override string ToString() => $"{TopLeft} {TopRight} {BottomLeft} {BottomRight}"; 139 } 140}