< Summary

Information
Class: FixedMathSharp.Bounds.FixedTriangle2d
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Primitives/FixedTriangle2d.cs
Line coverage
100%
Covered lines: 78
Uncovered lines: 0
Coverable lines: 78
Total lines: 347
Line coverage: 100%
Branch coverage
97%
Covered branches: 35
Total branches: 36
Branch coverage: 97.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_SignedArea()100%11100%
get_Area()100%11100%
get_Bounds()100%11100%
get_Centroid()100%11100%
get_IsDegenerate()100%11100%
GetVertex(...)100%44100%
GetEdge(...)100%44100%
GetPoint(...)100%11100%
TryGetBarycentricWeights(...)100%22100%
Contains(...)100%1212100%
ClosestPoint(...)100%22100%
DistanceSquared(...)100%11100%
Deconstruct(...)100%11100%
op_Equality(...)100%11100%
op_Inequality(...)100%11100%
Equals(...)100%44100%
Equals(...)50%22100%
GetHashCode()100%11100%
IsPointOnAnyEdge(...)100%44100%
ClosestPointOnEdges(...)100%11100%
TrySetCloserPoint(...)100%22100%
ComponentMin(...)100%11100%
ComponentMax(...)100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Primitives/FixedTriangle2d.cs

#LineLine coverage
 1//=======================================================================
 2// FixedTriangle2d.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using MemoryPack;
 9using System;
 10using System.Runtime.CompilerServices;
 11using System.Text.Json.Serialization;
 12
 13namespace FixedMathSharp.Bounds;
 14
 15/// <summary>
 16/// Represents a triangle in two-dimensional fixed-point space.
 17/// </summary>
 18[Serializable]
 19[MemoryPackable]
 20public partial struct FixedTriangle2d : IEquatable<FixedTriangle2d>
 21{
 22    #region Constants
 23
 24    /// <summary>
 25    /// The number of vertices in a triangle.
 26    /// </summary>
 27    public const int VertexCount = 3;
 28
 29    /// <summary>
 30    /// The number of edges in a triangle.
 31    /// </summary>
 32    public const int EdgeCount = 3;
 33
 34    #endregion
 35
 36    #region Fields
 37
 38    /// <summary>
 39    /// The first vertex.
 40    /// </summary>
 41    [JsonInclude]
 42    [MemoryPackOrder(0)]
 43    public Vector2d A;
 44
 45    /// <summary>
 46    /// The second vertex.
 47    /// </summary>
 48    [JsonInclude]
 49    [MemoryPackOrder(1)]
 50    public Vector2d B;
 51
 52    /// <summary>
 53    /// The third vertex.
 54    /// </summary>
 55    [JsonInclude]
 56    [MemoryPackOrder(2)]
 57    public Vector2d C;
 58
 59    #endregion
 60
 61    #region Constructors
 62
 63    /// <summary>
 64    /// Initializes a triangle from ordered vertices.
 65    /// </summary>
 66    [JsonConstructor]
 67    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 68    public FixedTriangle2d(Vector2d a, Vector2d b, Vector2d c)
 69    {
 2370        A = a;
 2371        B = b;
 2372        C = c;
 2373    }
 74
 75    #endregion
 76
 77    #region Properties
 78
 79    /// <summary>
 80    /// The signed area of the triangle. Positive values indicate counter-clockwise winding.
 81    /// </summary>
 82    [JsonIgnore]
 83    [MemoryPackIgnore]
 84    public Fixed64 SignedArea
 85    {
 86        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3287        get => Vector2d.CrossProduct(B - A, C - A) * Fixed64.Half;
 88    }
 89
 90    /// <summary>
 91    /// The non-negative area of the triangle.
 92    /// </summary>
 93    [JsonIgnore]
 94    [MemoryPackIgnore]
 95    public Fixed64 Area
 96    {
 97        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 798        get => FixedMath.Abs(SignedArea);
 99    }
 100
 101    /// <summary>
 102    /// The normalized axis-aligned area that contains all vertices.
 103    /// </summary>
 104    [JsonIgnore]
 105    [MemoryPackIgnore]
 106    public FixedBoundArea Bounds
 107    {
 108        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2109        get => FixedBoundArea.FromMinMax(ComponentMin(ComponentMin(A, B), C), ComponentMax(ComponentMax(A, B), C));
 110    }
 111
 112    /// <summary>
 113    /// The arithmetic center of the three vertices.
 114    /// </summary>
 115    [JsonIgnore]
 116    [MemoryPackIgnore]
 117    public Vector2d Centroid
 118    {
 119        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1120        get => (A + B + C) / (Fixed64)3;
 121    }
 122
 123    /// <summary>
 124    /// Returns true when the triangle has no positive area.
 125    /// </summary>
 126    [JsonIgnore]
 127    [MemoryPackIgnore]
 128    public bool IsDegenerate
 129    {
 130        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3131        get => Area <= Fixed64.Epsilon;
 132    }
 133
 134    #endregion
 135
 136    #region Geometry Access
 137
 138    /// <summary>
 139    /// Gets a vertex by stable index: 0 = A, 1 = B, 2 = C.
 140    /// </summary>
 141    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 142    public Vector2d GetVertex(int index) =>
 5143        index switch
 5144        {
 1145            0 => A,
 1146            1 => B,
 1147            2 => C,
 2148            _ => throw new ArgumentOutOfRangeException(nameof(index), $"Vertex index must be between 0 and {VertexCount 
 5149        };
 150
 151    /// <summary>
 152    /// Gets an edge by stable index: 0 = AB, 1 = BC, 2 = CA.
 153    /// </summary>
 154    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 155    public FixedSegment2d GetEdge(int index) =>
 42156        index switch
 42157        {
 15158            0 => new FixedSegment2d(A, B),
 13159            1 => new FixedSegment2d(B, C),
 12160            2 => new FixedSegment2d(C, A),
 2161            _ => throw new ArgumentOutOfRangeException(nameof(index), $"Edge index must be between 0 and {EdgeCount - 1}
 42162        };
 163
 164    /// <summary>
 165    /// Gets the point represented by barycentric weights for vertices B and C.
 166    /// </summary>
 167    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 168    public Vector2d GetPoint(Fixed64 weightB, Fixed64 weightC)
 169    {
 6170        return Vector2d.BarycentricCoordinates(A, B, C, weightB, weightC);
 171    }
 172
 173    #endregion
 174
 175    #region Spatial Queries
 176
 177    /// <summary>
 178    /// Computes barycentric weights for a point relative to this triangle.
 179    /// </summary>
 180    /// <returns>
 181    /// True when the triangle has non-degenerate area; false when weights cannot be solved.
 182    /// </returns>
 183    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 184    public bool TryGetBarycentricWeights(Vector2d point, out Fixed64 weightA, out Fixed64 weightB, out Fixed64 weightC)
 185    {
 3186        Vector2d ab = B - A;
 3187        Vector2d ac = C - A;
 3188        Fixed64 denominator = Vector2d.CrossProduct(ab, ac);
 3189        if (FixedMath.Abs(denominator) <= Fixed64.Epsilon)
 190        {
 1191            weightA = Fixed64.Zero;
 1192            weightB = Fixed64.Zero;
 1193            weightC = Fixed64.Zero;
 1194            return false;
 195        }
 196
 2197        Vector2d ap = point - A;
 2198        weightB = Vector2d.CrossProduct(ap, ac) / denominator;
 2199        weightC = Vector2d.CrossProduct(ab, ap) / denominator;
 2200        weightA = Fixed64.One - weightB - weightC;
 2201        return true;
 202    }
 203
 204    /// <summary>
 205    /// Determines whether the point is inside the triangle, including edges and vertices.
 206    /// </summary>
 207    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 208    public bool Contains(Vector2d point)
 209    {
 22210        Fixed64 signedArea = SignedArea;
 22211        if (FixedMath.Abs(signedArea) <= Fixed64.Epsilon)
 7212            return IsPointOnAnyEdge(point);
 213
 15214        Fixed64 ab = Vector2d.CrossProduct(B - A, point - A);
 15215        Fixed64 bc = Vector2d.CrossProduct(C - B, point - B);
 15216        Fixed64 ca = Vector2d.CrossProduct(A - C, point - C);
 217
 15218        return signedArea > Fixed64.Zero
 15219            ? ab >= -Fixed64.Epsilon && bc >= -Fixed64.Epsilon && ca >= -Fixed64.Epsilon
 15220            : ab <= Fixed64.Epsilon && bc <= Fixed64.Epsilon && ca <= Fixed64.Epsilon;
 221    }
 222
 223    /// <summary>
 224    /// Finds the closest point on or inside this triangle to the supplied point.
 225    /// </summary>
 226    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 227    public Vector2d ClosestPoint(Vector2d point)
 228    {
 9229        return Contains(point) ? point : ClosestPointOnEdges(point);
 230    }
 231
 232    /// <summary>
 233    /// Computes the squared distance from the supplied point to this triangle.
 234    /// </summary>
 235    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 236    public Fixed64 DistanceSquared(Vector2d point)
 237    {
 3238        return Vector2d.DistanceSquared(point, ClosestPoint(point));
 239    }
 240
 241    #endregion
 242
 243    #region Deconstruction
 244
 245    /// <summary>
 246    /// Deconstructs the triangle into ordered vertices.
 247    /// </summary>
 248    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 249    public void Deconstruct(out Vector2d a, out Vector2d b, out Vector2d c)
 250    {
 1251        a = A;
 1252        b = B;
 1253        c = C;
 1254    }
 255
 256    #endregion
 257
 258    #region Operators
 259
 260    /// <summary>
 261    /// Determines whether two triangles have the same ordered vertices.
 262    /// </summary>
 263    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2264    public static bool operator ==(FixedTriangle2d left, FixedTriangle2d right) => left.Equals(right);
 265
 266    /// <summary>
 267    /// Determines whether two triangles have different ordered vertices.
 268    /// </summary>
 269    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2270    public static bool operator !=(FixedTriangle2d left, FixedTriangle2d right) => !left.Equals(right);
 271
 272    #endregion
 273
 274    #region Equality
 275
 276    /// <inheritdoc />
 277    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 278    public bool Equals(FixedTriangle2d other)
 279    {
 7280        return A == other.A && B == other.B && C == other.C;
 281    }
 282
 283    /// <inheritdoc />
 284    public override bool Equals(object? obj)
 285    {
 1286        return obj is FixedTriangle2d other && Equals(other);
 287    }
 288
 289    /// <inheritdoc />
 290    public override int GetHashCode()
 291    {
 292        unchecked
 293        {
 2294            int hash = 17;
 2295            hash = (hash * 31) + A.StateHash;
 2296            hash = (hash * 31) + B.StateHash;
 2297            hash = (hash * 31) + C.StateHash;
 2298            return hash;
 299        }
 300    }
 301
 302    #endregion
 303
 304    #region Helpers
 305
 306    private bool IsPointOnAnyEdge(Vector2d point)
 307    {
 7308        return GetEdge(0).DistanceSquared(point) <= Fixed64.Epsilon
 7309            || GetEdge(1).DistanceSquared(point) <= Fixed64.Epsilon
 7310            || GetEdge(2).DistanceSquared(point) <= Fixed64.Epsilon;
 311    }
 312
 313    private Vector2d ClosestPointOnEdges(Vector2d point)
 314    {
 7315        Vector2d best = GetEdge(0).ClosestPoint(point);
 7316        Fixed64 bestDistance = Vector2d.DistanceSquared(point, best);
 7317        TrySetCloserPoint(GetEdge(1), point, ref best, ref bestDistance);
 7318        TrySetCloserPoint(GetEdge(2), point, ref best, ref bestDistance);
 7319        return best;
 320    }
 321
 322    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 323    private static void TrySetCloserPoint(FixedSegment2d edge, Vector2d point, ref Vector2d best, ref Fixed64 bestDistan
 324    {
 14325        Vector2d candidate = edge.ClosestPoint(point);
 14326        Fixed64 distance = Vector2d.DistanceSquared(point, candidate);
 14327        if (distance >= bestDistance)
 11328            return;
 329
 3330        bestDistance = distance;
 3331        best = candidate;
 3332    }
 333
 334    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 335    private static Vector2d ComponentMin(Vector2d a, Vector2d b)
 336    {
 4337        return new Vector2d(FixedMath.Min(a.X, b.X), FixedMath.Min(a.Y, b.Y));
 338    }
 339
 340    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 341    private static Vector2d ComponentMax(Vector2d a, Vector2d b)
 342    {
 4343        return new Vector2d(FixedMath.Max(a.X, b.X), FixedMath.Max(a.Y, b.Y));
 344    }
 345
 346    #endregion
 347}