A game framework written with osu! in mind.
at master 170 lines 7.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 osu.Framework.Utils; 7using osuTK; 8 9namespace osu.Framework.Graphics.Primitives 10{ 11 /// <summary> 12 /// Represents a single line segment. 13 /// </summary> 14 public readonly struct Line 15 { 16 /// <summary> 17 /// Begin point of the line. 18 /// </summary> 19 public readonly Vector2 StartPoint; 20 21 /// <summary> 22 /// End point of the line. 23 /// </summary> 24 public readonly Vector2 EndPoint; 25 26 /// <summary> 27 /// The length of the line. 28 /// </summary> 29 public float Rho => (EndPoint - StartPoint).Length; 30 31 /// <summary> 32 /// The direction of the second point from the first. 33 /// </summary> 34 public float Theta => MathF.Atan2(EndPoint.Y - StartPoint.Y, EndPoint.X - StartPoint.X); 35 36 /// <summary> 37 /// The direction of this <see cref="Line"/>. 38 /// </summary> 39 public Vector2 Direction => EndPoint - StartPoint; 40 41 /// <summary> 42 /// The normalized direction of this <see cref="Line"/>. 43 /// </summary> 44 public Vector2 DirectionNormalized => Direction.Normalized(); 45 46 public Vector2 OrthogonalDirection 47 { 48 get 49 { 50 Vector2 dir = DirectionNormalized; 51 return new Vector2(-dir.Y, dir.X); 52 } 53 } 54 55 public Line(Vector2 p1, Vector2 p2) 56 { 57 StartPoint = p1; 58 EndPoint = p2; 59 } 60 61 /// <summary> 62 /// Computes a position along this line. 63 /// </summary> 64 /// <param name="t">A parameter representing the position along the line to compute. 0 yields the start point and 1 yields the end point.</param> 65 /// <returns>The position along the line.</returns> 66 [MethodImpl(MethodImplOptions.AggressiveInlining)] 67 public Vector2 At(float t) => new Vector2(StartPoint.X + (EndPoint.X - StartPoint.X) * t, StartPoint.Y + (EndPoint.Y - StartPoint.Y) * t); 68 69 /// <summary> 70 /// Intersects this line with another. 71 /// </summary> 72 /// <param name="other">The line to intersect with.</param> 73 /// <returns>Whether the two lines intersect and, if so, the distance along this line at which the intersection occurs. 74 /// An intersection may occur even if the two lines don't touch, at which point the parameter will be outside the [0, 1] range. 75 /// To compute the point of intersection, <see cref="At"/>.</returns> 76 [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 public (bool success, float distance) IntersectWith(in Line other) 78 { 79 bool success = TryIntersectWith(other, out var distance); 80 return (success, distance); 81 } 82 83 /// <summary> 84 /// Intersects this line with another. 85 /// </summary> 86 /// <param name="other">The line to intersect with.</param> 87 /// <param name="distance">The distance along this line at which the intersection occurs. To compute the point of intersection, <see cref="At"/>.</param> 88 /// <returns>Whether the two lines intersect. An intersection may occur even if the two lines don't touch, at which point the parameter will be outside the [0, 1] range.</returns> 89 [MethodImpl(MethodImplOptions.AggressiveInlining)] 90 public bool TryIntersectWith(in Line other, out float distance) 91 { 92 var startPoint = other.StartPoint; 93 var endPoint = other.EndPoint; 94 95 return TryIntersectWith(ref startPoint, ref endPoint, out distance); 96 } 97 98 /// <summary> 99 /// Intersects this line with another. 100 /// </summary> 101 /// <param name="otherStart">The start point of the other line to intersect with.</param> 102 /// <param name="otherEnd">The end point of the other line to intersect with.</param> 103 /// <param name="distance">The distance along this line at which the intersection occurs. To compute the point of intersection, <see cref="At"/>.</param> 104 /// <returns>Whether the two lines intersect. An intersection may occur even if the two lines don't touch, at which point the parameter will be outside the [0, 1] range.</returns> 105 [MethodImpl(MethodImplOptions.AggressiveInlining)] 106 public bool TryIntersectWith(ref Vector2 otherStart, ref Vector2 otherEnd, out float distance) 107 { 108 float otherYDist = otherEnd.Y - otherStart.Y; 109 float otherXDist = otherEnd.X - otherStart.X; 110 111 float denom = (EndPoint.X - StartPoint.X) * otherYDist - (EndPoint.Y - StartPoint.Y) * otherXDist; 112 113 if (Precision.AlmostEquals(denom, 0)) 114 { 115 distance = 0; 116 return false; 117 } 118 119 distance = ((otherStart.X - StartPoint.X) * otherYDist - (otherStart.Y - StartPoint.Y) * otherXDist) / denom; 120 return true; 121 } 122 123 /// <summary> 124 /// Distance squared from an arbitrary point p to this line. 125 /// </summary> 126 public float DistanceSquaredToPoint(Vector2 p) => Vector2Extensions.DistanceSquared(p, ClosestPointTo(p)); 127 128 /// <summary> 129 /// Distance from an arbitrary point to this line. 130 /// </summary> 131 public float DistanceToPoint(Vector2 p) => Vector2Extensions.Distance(p, ClosestPointTo(p)); 132 133 /// <summary> 134 /// Finds the point closest to the given point on this line. 135 /// </summary> 136 /// <remarks> 137 /// See http://geometryalgorithms.com/Archive/algorithm_0102/algorithm_0102.htm, near the bottom. 138 /// </remarks> 139 public Vector2 ClosestPointTo(Vector2 p) 140 { 141 Vector2 v = EndPoint - StartPoint; // Vector from line's p1 to p2 142 Vector2 w = p - StartPoint; // Vector from line's p1 to p 143 144 // See if p is closer to p1 than to the segment 145 float c1 = Vector2.Dot(w, v); 146 if (c1 <= 0) 147 return StartPoint; 148 149 // See if p is closer to p2 than to the segment 150 float c2 = Vector2.Dot(v, v); 151 if (c2 <= c1) 152 return EndPoint; 153 154 // p is closest to point pB, between p1 and p2 155 float b = c1 / c2; 156 Vector2 pB = StartPoint + b * v; 157 158 return pB; 159 } 160 161 public Matrix4 WorldMatrix() => Matrix4.CreateRotationZ(Theta) * Matrix4.CreateTranslation(StartPoint.X, StartPoint.Y, 0); 162 163 /// <summary> 164 /// It's the end of the world as we know it 165 /// </summary> 166 public Matrix4 EndWorldMatrix() => Matrix4.CreateRotationZ(Theta) * Matrix4.CreateTranslation(EndPoint.X, EndPoint.Y, 0); 167 168 public override string ToString() => $"{StartPoint} -> {EndPoint}"; 169 } 170}