A game framework written with osu! in mind.
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 osuTK;
5using System;
6using System.Runtime.CompilerServices;
7using System.Runtime.InteropServices;
8
9namespace osu.Framework.Graphics.Primitives
10{
11 [StructLayout(LayoutKind.Sequential)]
12 public readonly struct Triangle : IConvexPolygon, IEquatable<Triangle>
13 {
14 // Note: Do not change the order of vertices. They are ordered in screen-space counter-clockwise fashion.
15 // See: IPolygon.GetVertices()
16 public readonly Vector2 P0;
17 public readonly Vector2 P1;
18 public readonly Vector2 P2;
19
20 public Triangle(Vector2 p0, Vector2 p1, Vector2 p2)
21 {
22 P0 = p0;
23 P1 = p1;
24 P2 = p2;
25 }
26
27 public ReadOnlySpan<Vector2> GetAxisVertices() => GetVertices();
28
29 public ReadOnlySpan<Vector2> GetVertices() => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in P0), 3);
30
31 public bool Equals(Triangle other) =>
32 P0 == other.P0 &&
33 P1 == other.P1 &&
34 P2 == other.P2;
35
36 /// <summary>
37 /// Checks whether a point lies within the triangle.
38 /// </summary>
39 /// <param name="pos">The point to check.</param>
40 /// <returns>Outcome of the check.</returns>
41 public bool Contains(Vector2 pos)
42 {
43 // This code parametrizes pos as a linear combination of 2 edges s*(p1-p0) + t*(p2->p0).
44 // pos is contained if s>0, t>0, s+t<1
45 float area2 = P0.Y * (P2.X - P1.X) + P0.X * (P1.Y - P2.Y) + P1.X * P2.Y - P1.Y * P2.X;
46 if (area2 == 0)
47 return false;
48
49 float s = (P0.Y * P2.X - P0.X * P2.Y + (P2.Y - P0.Y) * pos.X + (P0.X - P2.X) * pos.Y) / area2;
50 if (s < 0)
51 return false;
52
53 float t = (P0.X * P1.Y - P0.Y * P1.X + (P0.Y - P1.Y) * pos.X + (P1.X - P0.X) * pos.Y) / area2;
54 if (t < 0 || s + t > 1)
55 return false;
56
57 return true;
58 }
59
60 public RectangleF AABBFloat
61 {
62 get
63 {
64 float xMin = Math.Min(P0.X, Math.Min(P1.X, P2.X));
65 float yMin = Math.Min(P0.Y, Math.Min(P1.Y, P2.Y));
66 float xMax = Math.Max(P0.X, Math.Max(P1.X, P2.X));
67 float yMax = Math.Max(P0.Y, Math.Max(P1.Y, P2.Y));
68
69 return new RectangleF(xMin, yMin, xMax - xMin, yMax - yMin);
70 }
71 }
72
73 public float Area => 0.5f * Math.Abs(Vector2Extensions.GetOrientation(GetVertices()));
74 }
75}