< Summary

Line coverage
100%
Covered lines: 717
Uncovered lines: 0
Coverable lines: 717
Total lines: 1705
Line coverage: 100%
Branch coverage
100%
Covered branches: 200
Total branches: 200
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor(...)100%11100%
File 1: get_UnnormalizedNormal()100%11100%
File 1: get_Normal()100%44100%
File 1: get_Area()100%11100%
File 1: get_Bounds()100%11100%
File 1: get_Centroid()100%11100%
File 1: get_IsDegenerate()100%11100%
File 1: GetVertex(...)100%44100%
File 1: GetEdge(...)100%44100%
File 1: GetPoint(...)100%11100%
File 1: TryGetProjectedBarycentricWeights(...)100%22100%
File 1: GetClosestPointAnchor(...)100%44100%
File 1: ContainsProjection(...)100%66100%
File 1: Contains(...)100%11100%
File 1: ClosestPoint(...)100%1616100%
File 1: TryGetClosestPointBarycentrics(...)100%3232100%
File 1: DistanceSquared(...)100%11100%
File 1: Deconstruct(...)100%11100%
File 1: op_Equality(...)100%11100%
File 1: op_Inequality(...)100%11100%
File 1: Equals(...)100%44100%
File 1: Equals(...)100%22100%
File 1: GetHashCode()100%11100%
File 1: ClosestPointOnEdges(...)100%11100%
File 1: TrySetCloserPoint(...)100%22100%
File 1: GetExactNormal(...)100%11100%
File 1: GetExactNormalComponents(...)100%11100%
File 1: GetDifferenceDot(...)100%11100%
File 1: ComponentMin(...)100%11100%
File 1: ComponentMax(...)100%11100%
File 2: TryGetCircleSlabContact(...)100%11100%
File 2: TryGetCenteredCapsuleSlabContact(...)100%11100%
File 2: ValidateCapsuleSlab(...)100%88100%
File 2: TryGetClosestPointsToCenteredAxis(...)100%11100%
File 2: DoesCenteredCapsuleOverlap(...)100%22100%
File 2: TryGetCenteredCapsuleContact(...)100%11100%
File 2: ValidateCenteredCapsuleContact(...)100%44100%
File 2: ValidateCenteredAxis(...)100%44100%
File 2: TryGetFiniteConeIntersectionMinimumAxialPoint(...)100%11100%
File 2: TryGetFiniteConeIntersectionMinimumAxialPoint(...)100%22100%
File 2: ValidateFiniteCone(...)100%66100%
File 2: TryGetFiniteSlabProjectedCircleContact(...)100%44100%
File 2: TryGetFiniteSlabProjectedCircleSweep(...)100%88100%
File 2: TryGetFiniteSlabProjectedCircleSweep(...)100%44100%
File 3: TryGetContact(...)100%44100%
File 4: TryGetCenteredFiniteCylinderContact(...)100%11100%
File 4: TryGetCenteredFiniteCylinderContact(...)100%44100%
File 4: TryGetCenteredFiniteCylinderSupportContact(...)100%11100%
File 4: TryGetCenteredFiniteConeContact(...)100%44100%
File 4: TryGetCenteredFiniteConeSupportContact(...)100%11100%
File 4: TryGetCenteredCapsuleContact(...)100%11100%
File 4: TryGetCenteredCapsuleContact(...)100%11100%
File 4: TryGetClippedCenteredCapsuleContact(...)100%11100%
File 4: TryGetCenteredCapsuleContact(...)100%44100%
File 4: TryGetClippedCenteredCapsuleContact(...)100%66100%
File 4: TryGetCenteredCapsuleContact(...)100%88100%
File 4: TryGetSphereContact(...)100%66100%
File 4: ValidateCenteredSurfaceContact(...)100%66100%
File 4: TryGetSphereContactInChart(...)100%1212100%
File 4: TryGetCenteredCapsuleContactInChart(...)100%1616100%
File 4: TryGetSupportContact(...)100%44100%
File 4: ValidateRigidTriangleFrame(...)100%22100%
File 4: ValidateRigidShapeFrame(...)100%22100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Primitives/Triangles/FixedTriangle.cs

#LineLine coverage
 1//=======================================================================
 2// FixedTriangle.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 System;
 9using System.Runtime.CompilerServices;
 10using System.Text.Json.Serialization;
 11using MemoryPack;
 12
 13namespace FixedMathSharp.Geometry;
 14
 15/// <summary>
 16/// Represents an ordered triangle in three-dimensional fixed-point space.
 17/// </summary>
 18/// <remarks>
 19/// Triangle queries preserve complete raw-coordinate differences and exact wide
 20/// predicates until their final public <see cref="Fixed64"/> conversion.
 21/// </remarks>
 22[Serializable]
 23[MemoryPackable]
 24public partial struct FixedTriangle : IEquatable<FixedTriangle>
 25{
 26    #region Constants
 27
 28    /// <summary>
 29    /// The number of vertices in a triangle.
 30    /// </summary>
 31    public const int VertexCount = 3;
 32
 33    /// <summary>
 34    /// The number of edges in a triangle.
 35    /// </summary>
 36    public const int EdgeCount = 3;
 37
 38    #endregion
 39
 40    #region Fields
 41
 42    /// <summary>
 43    /// The first vertex.
 44    /// </summary>
 45    [JsonInclude]
 46    [MemoryPackOrder(0)]
 47    public Vector3d A;
 48
 49    /// <summary>
 50    /// The second vertex.
 51    /// </summary>
 52    [JsonInclude]
 53    [MemoryPackOrder(1)]
 54    public Vector3d B;
 55
 56    /// <summary>
 57    /// The third vertex.
 58    /// </summary>
 59    [JsonInclude]
 60    [MemoryPackOrder(2)]
 61    public Vector3d C;
 62
 63    #endregion
 64
 65    #region Constructors
 66
 67    /// <summary>
 68    /// Initializes a triangle from ordered vertices.
 69    /// </summary>
 70    [JsonConstructor]
 71    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 72    public FixedTriangle(Vector3d a, Vector3d b, Vector3d c)
 73    {
 2023074        A = a;
 2023075        B = b;
 2023076        C = c;
 2023077    }
 78
 79    #endregion
 80
 81    #region Properties
 82
 83    /// <summary>
 84    /// The unnormalized triangle normal from <c>cross(B - A, C - A)</c>.
 85    /// </summary>
 86    /// <remarks>
 87    /// Each exact cross component is rounded once, half to even, and saturates
 88    /// independently at the public Q32.32 boundary.
 89    /// </remarks>
 90    [JsonIgnore]
 91    [MemoryPackIgnore]
 92    public Vector3d UnnormalizedNormal
 93    {
 94        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 95        get
 96        {
 997            GetExactNormalComponents(out Signed192 x, out Signed192 y, out Signed192 z);
 998            return new Vector3d(
 999                Fixed64.RoundSignedToFixed(x, FixedMath.SHIFT_AMOUNT_I),
 9100                Fixed64.RoundSignedToFixed(y, FixedMath.SHIFT_AMOUNT_I),
 9101                Fixed64.RoundSignedToFixed(z, FixedMath.SHIFT_AMOUNT_I));
 102        }
 103    }
 104
 105    /// <summary>
 106    /// The normalized triangle normal derived from the exact cross product.
 107    /// </summary>
 108    /// <remarks>
 109    /// Components are rounded half to even from the exact squared magnitude.
 110    /// Triangles at or below the inclusive degeneracy threshold return
 111    /// <see cref="Vector3d.Zero"/>.
 112    /// </remarks>
 113    [JsonIgnore]
 114    [MemoryPackIgnore]
 115    public Vector3d Normal
 116    {
 117        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 118        get
 119        {
 21120            GetExactNormalComponents(out Signed192 x, out Signed192 y, out Signed192 z);
 21121            Signed320 squaredMagnitude = WideGeometry.GetSquaredMagnitude(
 21122                x,
 21123                y,
 21124                z,
 21125                out Signed320 xSquare,
 21126                out Signed320 ySquare,
 21127                out Signed320 zSquare);
 21128            if (WideGeometry.IsQ128MagnitudeAtMostEpsilon(squaredMagnitude))
 5129                return Vector3d.Zero;
 130
 16131            Signed192 magnitude = WideArithmetic.GetFloorSquareRoot(squaredMagnitude, out Signed192 remainder);
 16132            Signed192 ceilingMagnitude = remainder.IsZero
 16133                ? magnitude
 16134                : WideArithmetic.AddSigned192(magnitude, Signed192.Signed(1L));
 16135            return new Vector3d(
 16136                Fixed64.NormalizeWideComponent(x, xSquare, ceilingMagnitude, squaredMagnitude),
 16137                Fixed64.NormalizeWideComponent(y, ySquare, ceilingMagnitude, squaredMagnitude),
 16138                Fixed64.NormalizeWideComponent(z, zSquare, ceilingMagnitude, squaredMagnitude));
 139        }
 140    }
 141
 142    /// <summary>
 143    /// The non-negative surface area of the triangle.
 144    /// </summary>
 145    /// <remarks>
 146    /// The exact cross-product magnitude is halved and rounded once, half to
 147    /// even. Results beyond the positive Q32.32 range saturate to
 148    /// <see cref="Fixed64.MaxValue"/>.
 149    /// </remarks>
 150    [JsonIgnore]
 151    [MemoryPackIgnore]
 152    public Fixed64 Area
 153    {
 154        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 155        get
 156        {
 16157            GetExactNormal(out _, out _, out _, out Signed320 squaredMagnitude);
 16158            Signed192 root = WideArithmetic.GetFloorSquareRoot(squaredMagnitude, out Signed192 remainder);
 16159            return Fixed64.RoundSquareRootToFixed(root, remainder, FixedMath.SHIFT_AMOUNT_I + 1);
 160        }
 161    }
 162
 163    /// <summary>
 164    /// The normalized axis-aligned box that contains all vertices.
 165    /// </summary>
 166    [JsonIgnore]
 167    [MemoryPackIgnore]
 168    public FixedBoundBox Bounds
 169    {
 170        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2171        get => FixedBoundBox.FromMinMax(ComponentMin(ComponentMin(A, B), C), ComponentMax(ComponentMax(A, B), C));
 172    }
 173
 174    /// <summary>
 175    /// The arithmetic center of the three vertices, rounded half to even per component.
 176    /// </summary>
 177    [JsonIgnore]
 178    [MemoryPackIgnore]
 179    public Vector3d Centroid
 180    {
 181        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 86182        get => new(
 86183            FixedMath.Average(A.X, B.X, C.X),
 86184            FixedMath.Average(A.Y, B.Y, C.Y),
 86185            FixedMath.Average(A.Z, B.Z, C.Z));
 186    }
 187
 188    /// <summary>
 189    /// Returns true when the exact squared normal magnitude is at or below
 190    /// the inclusive <see cref="Fixed64.Epsilon"/> threshold.
 191    /// </summary>
 192    [JsonIgnore]
 193    [MemoryPackIgnore]
 194    public bool IsDegenerate
 195    {
 196        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 197        get
 198        {
 938199            GetExactNormal(out _, out _, out _, out Signed320 squaredMagnitude);
 938200            return WideGeometry.IsQ128MagnitudeAtMostEpsilon(squaredMagnitude);
 201        }
 202    }
 203
 204    #endregion
 205
 206    #region Geometry Access
 207
 208    /// <summary>
 209    /// Gets a vertex by stable index: 0 = A, 1 = B, 2 = C.
 210    /// </summary>
 211    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 212    public Vector3d GetVertex(int index) =>
 5213        index switch
 5214        {
 1215            0 => A,
 1216            1 => B,
 1217            2 => C,
 2218            _ => throw new ArgumentOutOfRangeException(nameof(index), $"Vertex index must be between 0 and {VertexCount 
 5219        };
 220
 221    /// <summary>
 222    /// Gets an edge by stable index: 0 = AB, 1 = BC, 2 = CA.
 223    /// </summary>
 224    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 225    public FixedSegment GetEdge(int index) =>
 19838226        index switch
 19838227        {
 6864228            0 => new FixedSegment(A, B),
 6611229            1 => new FixedSegment(B, C),
 6361230            2 => new FixedSegment(C, A),
 2231            _ => throw new ArgumentOutOfRangeException(nameof(index), $"Edge index must be between 0 and {EdgeCount - 1}
 19838232        };
 233
 234    /// <summary>
 235    /// Gets the point represented by barycentric weights for vertices B and C
 236    /// without saturating intermediate differences.
 237    /// </summary>
 238    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 239    public Vector3d GetPoint(Fixed64 weightB, Fixed64 weightC) =>
 311240        Vector3d.BarycentricCoordinates(A, B, C, weightB, weightC);
 241
 242    #endregion
 243
 244    #region Spatial Queries
 245
 246    /// <summary>
 247    /// Computes barycentric weights for the point projected onto this triangle's plane.
 248    /// </summary>
 249    /// <returns>
 250    /// True when the exact Gram denominator is outside the inclusive
 251    /// <see cref="Fixed64.Epsilon"/> failure threshold; otherwise false with
 252    /// all three weights set to zero.
 253    /// </returns>
 254    /// <remarks>
 255    /// Successful weights are computed from independent exact numerators, then
 256    /// rounded half to even and saturated independently at the public boundary.
 257    /// The point is projected onto the triangle plane; it need not lie on it.
 258    /// </remarks>
 259    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 260    public bool TryGetProjectedBarycentricWeights(Vector3d point, out Fixed64 weightA, out Fixed64 weightB, out Fixed64 
 261    {
 22262        Signed192 abAb = GetDifferenceDot(B, A, B, A);
 22263        Signed192 abAc = GetDifferenceDot(B, A, C, A);
 22264        Signed192 acAc = GetDifferenceDot(C, A, C, A);
 22265        Signed192 apAb = GetDifferenceDot(point, A, B, A);
 22266        Signed192 apAc = GetDifferenceDot(point, A, C, A);
 22267        Signed320 denominator = WideArithmetic.MultiplySubtract(abAb, acAc, abAc, abAc);
 268
 22269        if (WideGeometry.IsQ128MagnitudeAtMostEpsilon(denominator))
 270        {
 3271            weightA = Fixed64.Zero;
 3272            weightB = Fixed64.Zero;
 3273            weightC = Fixed64.Zero;
 3274            return false;
 275        }
 276
 19277        Signed320 numeratorB = WideArithmetic.MultiplySubtract(acAc, apAb, abAc, apAc);
 19278        Signed320 numeratorC = WideArithmetic.MultiplySubtract(abAb, apAc, abAc, apAb);
 19279        Signed320 numeratorA = WideArithmetic.SubtractSigned320(
 19280            WideArithmetic.SubtractSigned320(denominator, numeratorB),
 19281            numeratorC);
 19282        weightA = Fixed64.GetSignedRatio(numeratorA, denominator);
 19283        weightB = Fixed64.GetSignedRatio(numeratorB, denominator);
 19284        weightC = Fixed64.GetSignedRatio(numeratorC, denominator);
 19285        return true;
 286    }
 287
 288    /// <summary>
 289    /// Gets the closest point on this triangle to a point carried by another
 290    /// rigid frame without materializing either absolute world point.
 291    /// </summary>
 292    /// <remarks>
 293    /// The returned anchor remains in the triangle's rigid frame. Voronoi
 294    /// predicates retain the complete relative-frame displacement until the
 295    /// final local barycentric conversion.
 296    /// </remarks>
 297    public FixedPointAnchor GetClosestPointAnchor(
 298        Vector3d triangleOrigin,
 299        FixedQuaternion triangleRotation,
 300        in FixedPointAnchor point)
 301    {
 94302        if (!triangleRotation.IsNormalized())
 303        {
 1304            throw new ArgumentException(
 1305                "Triangle rotation must be normalized.",
 1306                nameof(triangleRotation));
 307        }
 93308        if (!point.Rotation.IsNormalized())
 309        {
 1310            throw new ArgumentException(
 1311                "Point rotation must be normalized.",
 1312                nameof(point));
 313        }
 314
 92315        return WideTriangleRelations.GetClosestPointAnchor(
 92316            this,
 92317            triangleOrigin,
 92318            triangleRotation,
 92319            point);
 320    }
 321
 322    /// <summary>
 323    /// Determines whether the supplied point projects within or onto this
 324    /// triangle without requiring the point to lie on its plane.
 325    /// </summary>
 326    /// <remarks>
 327    /// The projected barycentric signs are classified from exact wide
 328    /// numerators. Degenerate triangles have no projected face and return false.
 329    /// </remarks>
 330    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 331    public bool ContainsProjection(Vector3d point)
 332    {
 33333        Signed192 abAb = GetDifferenceDot(B, A, B, A);
 33334        Signed192 abAc = GetDifferenceDot(B, A, C, A);
 33335        Signed192 acAc = GetDifferenceDot(C, A, C, A);
 33336        Signed320 denominator = WideArithmetic.MultiplySubtract(abAb, acAc, abAc, abAc);
 33337        if (WideGeometry.IsQ128MagnitudeAtMostEpsilon(denominator))
 1338            return false;
 339
 32340        Signed192 apAb = GetDifferenceDot(point, A, B, A);
 32341        Signed192 apAc = GetDifferenceDot(point, A, C, A);
 32342        Signed320 numeratorB = WideArithmetic.MultiplySubtract(acAc, apAb, abAc, apAc);
 32343        if (numeratorB.Sign < 0)
 1344            return false;
 345
 31346        Signed320 numeratorC = WideArithmetic.MultiplySubtract(abAb, apAc, abAc, apAb);
 31347        if (numeratorC.Sign < 0)
 2348            return false;
 349
 29350        return WideArithmetic.SubtractSigned320(
 29351            WideArithmetic.SubtractSigned320(denominator, numeratorB),
 29352            numeratorC).Sign >= 0;
 353    }
 354
 355    /// <summary>
 356    /// Determines whether the point is within the inclusive squared-distance
 357    /// epsilon of this triangle.
 358    /// </summary>
 359    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 360    public bool Contains(Vector3d point)
 361    {
 20362        return DistanceSquared(point) <= Fixed64.Epsilon;
 363    }
 364
 365    /// <summary>
 366    /// Finds the closest point on or inside this triangle to the supplied point.
 367    /// </summary>
 368    /// <remarks>
 369    /// Voronoi-region predicates and degenerate edge distances use exact wide
 370    /// intermediates. Degenerate candidates are visited in AB, BC, CA order,
 371    /// and an exact distance tie retains the first candidate.
 372    /// </remarks>
 373    public Vector3d ClosestPoint(Vector3d point)
 374    {
 1015375        if (!TryGetClosestPointBarycentrics(point, out Signed320 numeratorB,
 1015376            out Signed320 numeratorC, out Signed320 denominator, out int edgeMask))
 16377            return ClosestPointOnEdges(point);
 378
 1062379        if (edgeMask == 5) return A;
 977380        if (edgeMask == 3) return B;
 936381        if (edgeMask == 6) return C;
 382        // Preserve the public edge's single Lerp rounding; a face retains its
 383        // independent barycentric rounding before the public GetPoint operation.
 854384        if (edgeMask != 0)
 385        {
 671386            Signed320 edgeNumerator = edgeMask == 1 ? numeratorB : numeratorC;
 671387            _ = Fixed64.TryGetUnitIntervalRatio(Signed192.NarrowProven(edgeNumerator),
 671388                Signed192.NarrowProven(denominator), out Fixed64 parameter);
 671389            return edgeMask == 1 ? Vector3d.Lerp(A, B, parameter)
 671390                : edgeMask == 4 ? Vector3d.Lerp(A, C, parameter) : Vector3d.Lerp(B, C, parameter);
 391        }
 183392        _ = Fixed64.TryGetUnitIntervalRatio(numeratorB, denominator, out Fixed64 weightB);
 183393        _ = Fixed64.TryGetUnitIntervalRatio(numeratorC, denominator, out Fixed64 weightC);
 183394        return GetPoint(weightB, weightC);
 395    }
 396
 397    /// <summary>
 398    /// Selects the closest Voronoi feature without rounding its barycentric
 399    /// numerators. Epsilon-small triangles defer to the caller's edge reducer.
 400    /// </summary>
 401    internal bool TryGetClosestPointBarycentrics(Vector3d point,
 402        out Signed320 numeratorB, out Signed320 numeratorC,
 403        out Signed320 denominator, out int edgeMask)
 404    {
 3174405        numeratorB = default; numeratorC = default; edgeMask = 0;
 1058406        Signed192 abAb = GetDifferenceDot(B, A, B, A);
 1058407        Signed192 abAc = GetDifferenceDot(B, A, C, A);
 1058408        Signed192 acAc = GetDifferenceDot(C, A, C, A);
 1058409        denominator = WideArithmetic.MultiplySubtract(abAb, acAc, abAc, abAc);
 1058410        if (WideGeometry.IsQ128MagnitudeAtMostEpsilon(denominator))
 31411            return false;
 412
 1027413        Signed192 d1 = GetDifferenceDot(B, A, point, A);
 1027414        Signed192 d2 = GetDifferenceDot(C, A, point, A);
 1027415        if (d1.Sign <= 0 && d2.Sign <= 0)
 416        {
 132417            denominator = Signed320.ExtendValue(Signed192.Signed(1)); edgeMask = 5;
 66418            return true;
 419        }
 961420        Signed192 d3 = WideArithmetic.SubtractSigned192(d1, abAb);
 961421        Signed192 d4 = WideArithmetic.SubtractSigned192(d2, abAc);
 961422        if (d3.Sign >= 0 && WideArithmetic.SubtractSigned192(d4, d3).Sign <= 0)
 423        {
 129424            denominator = Signed320.ExtendValue(Signed192.Signed(1)); numeratorB = denominator; edgeMask = 3;
 43425            return true;
 426        }
 918427        Signed320 vc = WideArithmetic.MultiplySubtract(d1, d4, d3, d2);
 918428        if (vc.Sign <= 0 && d1.Sign >= 0 && d3.Sign <= 0)
 429        {
 307430            numeratorB = Signed320.ExtendValue(d1);
 614431            denominator = Signed320.ExtendValue(WideArithmetic.SubtractSigned192(d1, d3)); edgeMask = 1;
 307432            return true;
 433        }
 611434        Signed192 d5 = WideArithmetic.SubtractSigned192(d1, abAc);
 611435        Signed192 d6 = WideArithmetic.SubtractSigned192(d2, acAc);
 611436        if (d6.Sign >= 0 && WideArithmetic.SubtractSigned192(d5, d6).Sign <= 0)
 437        {
 132438            denominator = Signed320.ExtendValue(Signed192.Signed(1)); numeratorC = denominator; edgeMask = 6;
 44439            return true;
 440        }
 567441        Signed320 vb = WideArithmetic.MultiplySubtract(d5, d2, d1, d6);
 567442        if (vb.Sign <= 0 && d2.Sign >= 0 && d6.Sign <= 0)
 443        {
 294444            numeratorC = Signed320.ExtendValue(d2);
 588445            denominator = Signed320.ExtendValue(WideArithmetic.SubtractSigned192(d2, d6)); edgeMask = 4;
 294446            return true;
 447        }
 273448        Signed320 va = WideArithmetic.MultiplySubtract(d3, d6, d5, d4);
 273449        Signed192 d4MinusD3 = WideArithmetic.SubtractSigned192(d4, d3);
 273450        Signed192 d5MinusD6 = WideArithmetic.SubtractSigned192(d5, d6);
 273451        if (va.Sign <= 0 && d4MinusD3.Sign >= 0 && d5MinusD6.Sign >= 0)
 452        {
 79453            numeratorC = Signed320.ExtendValue(d4MinusD3);
 79454            denominator = Signed320.ExtendValue(WideArithmetic.AddSigned192(d4MinusD3, d5MinusD6));
 158455            numeratorB = WideArithmetic.SubtractSigned320(denominator, numeratorC); edgeMask = 2;
 79456            return true;
 457        }
 388458        numeratorB = vb; numeratorC = vc;
 194459        return true;
 460    }
 461
 462    /// <summary>
 463    /// Computes the squared distance from the supplied point to this triangle,
 464    /// rounded once and positively saturated at the public boundary.
 465    /// </summary>
 466    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 467    public Fixed64 DistanceSquared(Vector3d point)
 468    {
 28469        Vector3d closest = ClosestPoint(point);
 28470        Signed192 squaredDistance = GetDifferenceDot(point, closest, point, closest);
 28471        return Fixed64.RoundSquaredDistance(squaredDistance);
 472    }
 473
 474    #endregion
 475
 476    #region Deconstruction
 477
 478    /// <summary>
 479    /// Deconstructs the triangle into ordered vertices.
 480    /// </summary>
 481    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 482    public void Deconstruct(out Vector3d a, out Vector3d b, out Vector3d c)
 483    {
 1484        a = A;
 1485        b = B;
 1486        c = C;
 1487    }
 488
 489    #endregion
 490
 491    #region Operators
 492
 493    /// <summary>
 494    /// Determines whether two triangles have the same ordered vertices.
 495    /// </summary>
 496    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2497    public static bool operator ==(FixedTriangle left, FixedTriangle right) => left.Equals(right);
 498
 499    /// <summary>
 500    /// Determines whether two triangles have different ordered vertices.
 501    /// </summary>
 502    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2503    public static bool operator !=(FixedTriangle left, FixedTriangle right) => !left.Equals(right);
 504
 505    #endregion
 506
 507    #region Equality
 508
 509    /// <inheritdoc />
 510    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 511    public bool Equals(FixedTriangle other)
 512    {
 10513        return A == other.A && B == other.B && C == other.C;
 514    }
 515
 516    /// <inheritdoc />
 517    public override bool Equals(object? obj)
 518    {
 2519        return obj is FixedTriangle other && Equals(other);
 520    }
 521
 522    /// <inheritdoc />
 523    public override int GetHashCode()
 524    {
 525        unchecked
 526        {
 2527            int hash = 17;
 2528            hash = (hash * 31) + A.StateHash;
 2529            hash = (hash * 31) + B.StateHash;
 2530            hash = (hash * 31) + C.StateHash;
 2531            return hash;
 532        }
 533    }
 534
 535    #endregion
 536
 537    #region Helpers
 538
 539    private Vector3d ClosestPointOnEdges(Vector3d point)
 540    {
 16541        Vector3d best = GetEdge(0).ClosestPoint(point);
 16542        TrySetCloserPoint(GetEdge(1), point, ref best);
 16543        TrySetCloserPoint(GetEdge(2), point, ref best);
 16544        return best;
 545    }
 546
 547    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 548    private static void TrySetCloserPoint(FixedSegment edge, Vector3d point, ref Vector3d best)
 549    {
 32550        Vector3d candidate = edge.ClosestPoint(point);
 32551        if (WideGeometry.CompareSquaredDistance3D(
 32552            point.X, candidate.X, point.Y, candidate.Y, point.Z, candidate.Z,
 32553            point.X, best.X, point.Y, best.Y, point.Z, best.Z) >= 0)
 26554            return;
 555
 6556        best = candidate;
 6557    }
 558
 559    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 560    internal void GetExactNormal(
 561        out Signed192 x,
 562        out Signed192 y,
 563        out Signed192 z,
 564        out Signed320 squaredMagnitude)
 565    {
 13892566        GetExactNormalComponents(out x, out y, out z);
 13892567        squaredMagnitude = WideGeometry.GetSquaredMagnitude(x, y, z, out _, out _, out _);
 13892568    }
 569
 570    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 571    private void GetExactNormalComponents(
 572        out Signed192 x,
 573        out Signed192 y,
 574        out Signed192 z)
 575    {
 13922576        WideGeometry.GetDifferenceCrossProduct3D(
 13922577            B.X, A.X, B.Y, A.Y, B.Z, A.Z,
 13922578            C.X, A.X, C.Y, A.Y, C.Z, A.Z,
 13922579            out x,
 13922580            out y,
 13922581            out z);
 13922582    }
 583
 584    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 585    private static Signed192 GetDifferenceDot(
 586        Vector3d leftEnd,
 587        Vector3d leftStart,
 588        Vector3d rightEnd,
 589        Vector3d rightStart) =>
 5529590        WideGeometry.GetDifferenceDotProduct3D(
 5529591            leftEnd.X, leftStart.X, leftEnd.Y, leftStart.Y, leftEnd.Z, leftStart.Z,
 5529592            rightEnd.X, rightStart.X, rightEnd.Y, rightStart.Y, rightEnd.Z, rightStart.Z);
 593
 594    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 595    private static Vector3d ComponentMin(Vector3d a, Vector3d b)
 596    {
 4597        return new Vector3d(FixedMath.Min(a.X, b.X), FixedMath.Min(a.Y, b.Y), FixedMath.Min(a.Z, b.Z));
 598    }
 599
 600    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 601    private static Vector3d ComponentMax(Vector3d a, Vector3d b)
 602    {
 4603        return new Vector3d(FixedMath.Max(a.X, b.X), FixedMath.Max(a.Y, b.Y), FixedMath.Max(a.Z, b.Z));
 604    }
 605
 606    #endregion
 607}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Primitives/Triangles/FixedTriangle.FiniteShapeQueries.cs

#LineLine coverage
 1//=======================================================================
 2// FixedTriangle.FiniteShapeQueries.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 System;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Contains finite-axis, finite-cone, and finite-slab contact and sweep
 14/// queries for rigidly transformed triangles.
 15/// </content>
 16public partial struct FixedTriangle
 17{
 18    /// <summary>
 19    /// Attempts to construct canonical contact anchors between this rigidly
 20    /// transformed triangle and one vertical circle slab.
 21    /// </summary>
 22    public readonly bool TryGetCircleSlabContact(
 23        Vector3d triangleOrigin,
 24        FixedQuaternion triangleRotation,
 25        Vector3d slabCenter,
 26        Fixed64 circleFrameRotation,
 27        Fixed64 slabHalfThickness,
 28        Fixed64 circleRadius,
 29        out FixedContactAnchors contact)
 30    {
 9131        ValidateRigidTriangleFrame(triangleRotation);
 9132        ValidateCapsuleSlab(
 9133            Vector2d.Right,
 9134            Fixed64.Zero,
 9135            circleRadius,
 9136            slabHalfThickness);
 8937        return TriangleCylinderContact.TryGetContact(this, triangleOrigin, triangleRotation,
 8938            slabCenter, FixedQuaternion.FromAxisAngle(Vector3d.Up, -circleFrameRotation),
 8939            WideArithmetic.AddSigned192(Signed192.Raw(slabHalfThickness), Signed192.Raw(slabHalfThickness)),
 8940            circleRadius, out contact, out _);
 41    }
 42
 43    /// <summary>
 44    /// Attempts to construct canonical contact anchors between this rigidly
 45    /// transformed triangle and one vertical centered-capsule slab.
 46    /// </summary>
 47    /// <remarks>
 48    /// The slab is a planar capsule extruded through flat Y caps, not a rounded
 49    /// 3D capsule. Closed contact includes exact tangency. The complete cap,
 50    /// straight-side and rounded-end feature set determines the minimum depth
 51    /// before contact coordinates are rounded. A zero core reduces to a circle
 52    /// slab; degenerate triangles return false.
 53    /// </remarks>
 54    public readonly bool TryGetCenteredCapsuleSlabContact(
 55        Vector3d triangleOrigin,
 56        FixedQuaternion triangleRotation,
 57        Vector3d slabCenter,
 58        Fixed64 capsuleFrameRotation,
 59        Vector2d localCapsuleAxisDirection,
 60        Fixed64 capsuleAxisLength,
 61        Fixed64 capsuleRadius,
 62        Fixed64 slabHalfThickness,
 63        out FixedContactAnchors contact)
 64    {
 16065        ValidateRigidTriangleFrame(triangleRotation);
 16066        ValidateCapsuleSlab(
 16067            localCapsuleAxisDirection,
 16068            capsuleAxisLength,
 16069            capsuleRadius,
 16070            slabHalfThickness);
 15871        return TriangleCylinderContact.TryGetContact(this, triangleOrigin, triangleRotation,
 15872            slabCenter, FixedQuaternion.FromAxisAngle(Vector3d.Up, -capsuleFrameRotation),
 15873            WideArithmetic.AddSigned192(Signed192.Raw(slabHalfThickness), Signed192.Raw(slabHalfThickness)),
 15874            capsuleRadius, out contact, out _, localCapsuleAxisDirection, capsuleAxisLength);
 75    }
 76
 77    private static void ValidateCapsuleSlab(
 78        Vector2d localAxisDirection,
 79        Fixed64 axisLength,
 80        Fixed64 radius,
 81        Fixed64 halfThickness)
 82    {
 25183        if (!localAxisDirection.IsNormalized())
 84        {
 185            throw new ArgumentException(
 186                "Capsule axis direction must be normalized.",
 187                nameof(localAxisDirection));
 88        }
 25089        if (axisLength < Fixed64.Zero)
 190            throw new ArgumentOutOfRangeException(nameof(axisLength));
 24991        if (radius < Fixed64.Zero)
 192            throw new ArgumentOutOfRangeException(nameof(radius));
 24893        if (halfThickness <= Fixed64.Zero)
 194            throw new ArgumentOutOfRangeException(nameof(halfThickness));
 24795    }
 96
 97    /// <summary>
 98    /// Attempts to return the closest points on this triangle and a conceptual
 99    /// centered finite axis without materializing its endpoints.
 100    /// </summary>
 101    public readonly bool TryGetClosestPointsToCenteredAxis(
 102        Vector3d center,
 103        Vector3d axisDirection,
 104        Fixed64 axisLength,
 105        out Vector3d pointOnTriangle,
 106        out Vector3d pointOnAxis)
 107    {
 13108        ValidateCenteredAxis(axisDirection, axisLength);
 13109        return WideFiniteAxisIntersection.TryGetClosestPointsToCenteredAxis(
 13110            this,
 13111            center,
 13112            axisDirection,
 13113            axisLength,
 13114            out pointOnTriangle,
 13115            out pointOnAxis);
 116    }
 117
 118    /// <summary>
 119    /// Returns whether this triangle inclusively overlaps a conceptual centered
 120    /// capsule without materializing either contact witness.
 121    /// </summary>
 122    public readonly bool DoesCenteredCapsuleOverlap(
 123        Vector3d center,
 124        Vector3d axisDirection,
 125        Fixed64 axisLength,
 126        Fixed64 radius)
 127    {
 6128        ValidateCenteredAxis(axisDirection, axisLength);
 4129        if (radius < Fixed64.Zero)
 1130            throw new ArgumentOutOfRangeException(nameof(radius));
 131
 3132        return WideFiniteAxisIntersection.DoesCenteredCapsuleTriangleOverlap(
 3133            this,
 3134            center,
 3135            axisDirection,
 3136            axisLength,
 3137            radius);
 138    }
 139
 140    /// <summary>
 141    /// Attempts to create an inclusive contact between this triangle and a
 142    /// conceptual centered capsule.
 143    /// </summary>
 144    /// <remarks>
 145    /// The returned normal points from the triangle toward the capsule.
 146    /// <paramref name="fallbackNormal"/> is used only when the closest axis
 147    /// and triangle points coincide exactly. With a positive core length, the
 148    /// capsule witness is the first finite boundary exit from that witness
 149    /// opposite the selected normal. Its depth is this local ray distance,
 150    /// rather than a whole-triangle minimum translation. A false result means that the contact is
 151    /// separated or that at least one final output is not representable; use
 152    /// <see cref="DoesCenteredCapsuleOverlap"/> when classification must remain
 153    /// independent of witness materialization.
 154    /// </remarks>
 155    public readonly bool TryGetCenteredCapsuleContact(
 156        Vector3d center,
 157        Vector3d axisDirection,
 158        Fixed64 axisLength,
 159        Fixed64 radius,
 160        Vector3d fallbackNormal,
 161        out Vector3d pointOnTriangle,
 162        out Vector3d pointOnCapsule,
 163        out Vector3d normal,
 164        out Fixed64 depth)
 165    {
 19166        ValidateCenteredCapsuleContact(
 19167            axisDirection,
 19168            axisLength,
 19169            radius,
 19170            fallbackNormal);
 171
 17172        return WideFiniteAxisIntersection.TryGetCenteredCapsuleTriangleContact(
 17173            this,
 17174            center,
 17175            axisDirection,
 17176            axisLength,
 17177            radius,
 17178            fallbackNormal,
 17179            out pointOnTriangle,
 17180            out pointOnCapsule,
 17181            out normal,
 17182            out depth);
 183    }
 184
 185    private static void ValidateCenteredCapsuleContact(
 186        Vector3d axisDirection,
 187        Fixed64 axisLength,
 188        Fixed64 radius,
 189        Vector3d fallbackNormal)
 190    {
 159191        ValidateCenteredAxis(axisDirection, axisLength);
 159192        if (radius < Fixed64.Zero)
 1193            throw new ArgumentOutOfRangeException(nameof(radius));
 158194        if (!fallbackNormal.IsNormalized())
 1195            throw new ArgumentException("Fallback normal must be normalized.", nameof(fallbackNormal));
 157196    }
 197
 198    private static void ValidateCenteredAxis(
 199        Vector3d axisDirection,
 200        Fixed64 axisLength)
 201    {
 178202        if (!axisDirection.IsNormalized())
 1203            throw new ArgumentException("Axis direction must be normalized.", nameof(axisDirection));
 177204        if (axisLength < Fixed64.Zero)
 1205            throw new ArgumentOutOfRangeException(nameof(axisLength));
 176206    }
 207
 208    /// <summary>
 209    /// Finds the intersecting point with the smallest axial parameter in an
 210    /// apex-authored finite cone.
 211    /// </summary>
 212    /// <remarks>
 213    /// Boundary candidates are visited in AB, BC, CA, face order. Triangle-face
 214    /// roots use a <see cref="Fixed64.MaxValue"/>-scaled parameter lattice;
 215    /// exact wide predicates select the witness before one final point rounding.
 216    /// </remarks>
 217    public bool TryGetFiniteConeIntersectionMinimumAxialPoint(
 218        Vector3d apex,
 219        Vector3d apexToBaseDirection,
 220        Fixed64 height,
 221        Fixed64 baseRadius,
 222        out Vector3d point)
 223    {
 114224        ValidateFiniteCone(
 114225            apexToBaseDirection,
 114226            height,
 114227            baseRadius,
 114228            nameof(apexToBaseDirection),
 114229            nameof(height));
 230
 111231        return WideTriangleConeIntersection.TryGetMinimumAxialPoint(this,
 111232            Vector3d.Zero, FixedQuaternion.Identity, apex, apexToBaseDirection, height, baseRadius, out point);
 233    }
 234
 235    /// <summary>
 236    /// Finds the minimum-axial cone intersection while retaining the triangle's
 237    /// rigid frame and the cone's world-space definition.
 238    /// </summary>
 239    /// <remarks>
 240    /// Neither the cone apex nor the triangle vertices must fit in the other's
 241    /// scalar coordinate frame. Classification uses exact rational rotation;
 242    /// only the selected triangle-local witness is rounded.
 243    /// </remarks>
 244    public bool TryGetFiniteConeIntersectionMinimumAxialPoint(
 245        Vector3d triangleOrigin, FixedQuaternion triangleRotation,
 246        Vector3d apex, Vector3d apexToBaseDirection, Fixed64 height, Fixed64 baseRadius,
 247        out FixedPointAnchor point)
 248    {
 30249        ValidateRigidTriangleFrame(triangleRotation);
 29250        ValidateFiniteCone(apexToBaseDirection, height, baseRadius, nameof(apexToBaseDirection), nameof(height));
 26251        bool found = WideTriangleConeIntersection.TryGetMinimumAxialPoint(this,
 26252            triangleOrigin, triangleRotation, apex, apexToBaseDirection, height, baseRadius, out Vector3d localPoint);
 26253        point = found ? FixedPointAnchor.FromValidatedFrame(triangleOrigin, triangleRotation, localPoint) : default;
 26254        return found;
 255    }
 256
 257    private static void ValidateFiniteCone(
 258        Vector3d axisDirection,
 259        Fixed64 height,
 260        Fixed64 baseRadius,
 261        string axisParameterName,
 262        string heightParameterName)
 263    {
 143264        if (!axisDirection.IsNormalized())
 2265            throw new ArgumentException("Finite cone axis direction must be normalized.", axisParameterName);
 141266        if (height <= Fixed64.Zero)
 2267            throw new ArgumentOutOfRangeException(heightParameterName);
 139268        if (baseRadius < Fixed64.Zero)
 2269            throw new ArgumentOutOfRangeException(nameof(baseRadius));
 137270    }
 271
 272    /// <summary>
 273    /// Attempts to find a triangle witness where a world-X/Z circle overlaps
 274    /// the triangle portion inside one finite world-Y slab.
 275    /// </summary>
 276    public readonly bool TryGetFiniteSlabProjectedCircleContact(
 277        Vector3d triangleOrigin,
 278        FixedQuaternion triangleRotation,
 279        Vector2d circleCenter,
 280        Fixed64 circleRadius,
 281        Fixed64 slabCenterY,
 282        Fixed64 slabHalfThickness,
 283        out FixedPointAnchor triangleContact)
 284    {
 8285        ValidateRigidTriangleFrame(triangleRotation);
 7286        if (circleRadius < Fixed64.Zero)
 1287            throw new ArgumentOutOfRangeException(nameof(circleRadius));
 6288        if (slabHalfThickness < Fixed64.Zero)
 1289            throw new ArgumentOutOfRangeException(nameof(slabHalfThickness));
 290
 5291        return WideOrientedBox.TryGetFiniteSlabProjectedCircleSweep(
 5292            this,
 5293            triangleOrigin,
 5294            triangleRotation,
 5295            circleCenter,
 5296            Vector2d.Right,
 5297            Fixed64.Zero,
 5298            circleRadius,
 5299            slabCenterY,
 5300            slabHalfThickness,
 5301            out _,
 5302            out triangleContact);
 303    }
 304
 305    /// <summary>
 306    /// Finds the first distance where a circle swept in world X/Z reaches the
 307    /// triangle portion inside one finite world-Y slab.
 308    /// </summary>
 309    /// <remarks>
 310    /// Triangle transformation, slab clipping, and time-of-impact
 311    /// classification remain exact until the final Q32.32 distance and local
 312    /// triangle witness are rounded.
 313    /// </remarks>
 314    public readonly bool TryGetFiniteSlabProjectedCircleSweep(
 315        Vector3d triangleOrigin,
 316        FixedQuaternion triangleRotation,
 317        Vector2d circleStart,
 318        Vector2d normalizedDirection,
 319        Fixed64 maximumDistance,
 320        Fixed64 circleRadius,
 321        Fixed64 slabCenterY,
 322        Fixed64 slabHalfThickness,
 323        out Fixed64 distance,
 324        out FixedPointAnchor triangleContact)
 325    {
 111326        ValidateRigidTriangleFrame(triangleRotation);
 111327        if (!normalizedDirection.IsNormalized())
 328        {
 1329            throw new ArgumentException(
 1330                "Sweep direction must be normalized.",
 1331                nameof(normalizedDirection));
 332        }
 110333        if (maximumDistance < Fixed64.Zero)
 1334            throw new ArgumentOutOfRangeException(nameof(maximumDistance));
 109335        if (circleRadius < Fixed64.Zero)
 1336            throw new ArgumentOutOfRangeException(nameof(circleRadius));
 108337        if (slabHalfThickness < Fixed64.Zero)
 1338            throw new ArgumentOutOfRangeException(nameof(slabHalfThickness));
 339
 107340        return WideOrientedBox.TryGetFiniteSlabProjectedCircleSweep(
 107341            this,
 107342            triangleOrigin,
 107343            triangleRotation,
 107344            circleStart,
 107345            normalizedDirection,
 107346            maximumDistance,
 107347            circleRadius,
 107348            slabCenterY,
 107349            slabHalfThickness,
 107350            out distance,
 107351            out triangleContact);
 352    }
 353    /// <summary>
 354    /// Finds the first distance along an authored X/Z circle-center segment
 355    /// that reaches this triangle portion inside one finite world-Y slab.
 356    /// </summary>
 357    /// <remarks>
 358    /// Transformation, slab clipping and the authored segment remain exact;
 359    /// only the final physical distance and local triangle witness are rounded.
 360    /// </remarks>
 361    public readonly bool TryGetFiniteSlabProjectedCircleSweep(
 362        Vector3d triangleOrigin,
 363        FixedQuaternion triangleRotation,
 364        FixedSegment2d segment,
 365        Fixed64 totalDistance,
 366        Fixed64 circleRadius,
 367        Fixed64 slabCenterY,
 368        Fixed64 slabHalfThickness,
 369        out Fixed64 distance,
 370        out FixedPointAnchor triangleContact)
 371    {
 104372        ValidateRigidTriangleFrame(triangleRotation);
 104373        _ = segment.GetPointAtDistance(Fixed64.Zero, totalDistance);
 102374        if (circleRadius < Fixed64.Zero)
 1375            throw new ArgumentOutOfRangeException(nameof(circleRadius));
 101376        if (slabHalfThickness < Fixed64.Zero)
 1377            throw new ArgumentOutOfRangeException(nameof(slabHalfThickness));
 100378        return WideOrientedBox.TryGetFiniteSlabProjectedCircleSweep(
 100379            this, triangleOrigin, triangleRotation, segment, totalDistance,
 100380            circleRadius, slabCenterY, slabHalfThickness, out distance, out triangleContact);
 381    }
 382
 383}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Primitives/Triangles/FixedTriangle.PairContacts.cs

#LineLine coverage
 1//=======================================================================
 2// FixedTriangle.PairContacts.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 System;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Provides exact full-domain contact generation between two rigidly
 14/// transformed triangles.
 15/// </content>
 16public partial struct FixedTriangle
 17{
 18    /// <summary>
 19    /// Attempts to construct an exact full-domain contact relation between
 20    /// this triangle and <paramref name="second"/> in their respective rigid
 21    /// frames.
 22    /// </summary>
 23    /// <remarks>
 24    /// Exact touching is a contact with zero depth. The normal points from the
 25    /// first triangle toward the second, and each returned anchor remains in
 26    /// its input frame. The winning depth is rounded half to even once; an
 27    /// unrepresentable positive depth is returned as <see cref="Fixed64.MaxValue"/>
 28    /// with <see cref="FixedContactAnchors.DepthIsClamped"/> set.
 29    /// </remarks>
 30    /// <returns>
 31    /// <see langword="false"/> when either triangle has an exact-zero normal or
 32    /// when a separating axis has negative overlap; otherwise
 33    /// <see langword="true"/>.
 34    /// </returns>
 35    /// <exception cref="ArgumentException">
 36    /// <paramref name="firstRotation"/> is not normalized.
 37    /// </exception>
 38    /// <exception cref="ArgumentException">
 39    /// <paramref name="secondRotation"/> is not normalized.
 40    /// </exception>
 41    public readonly bool TryGetContact(
 42        Vector3d firstOrigin,
 43        FixedQuaternion firstRotation,
 44        Vector3d secondOrigin,
 45        FixedQuaternion secondRotation,
 46        FixedTriangle second,
 47        out FixedContactAnchors contact)
 48    {
 9049        if (!firstRotation.IsNormalized())
 50        {
 151            throw new ArgumentException(
 152                "The first triangle rotation must be normalized.",
 153                nameof(firstRotation));
 54        }
 8955        if (!secondRotation.IsNormalized())
 56        {
 157            throw new ArgumentException(
 158                "The second triangle rotation must be normalized.",
 159                nameof(secondRotation));
 60        }
 61
 8862        return WideTriangleRelations.TryGetContact(
 8863            this,
 8864            firstOrigin,
 8865            firstRotation,
 8866            second,
 8867            secondOrigin,
 8868            secondRotation,
 8869            out contact);
 70    }
 71}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Primitives/Triangles/FixedTriangle.RigidContacts.cs

#LineLine coverage
 1//=======================================================================
 2// FixedTriangle.RigidContacts.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 System;
 9
 10namespace FixedMathSharp.Geometry;
 11
 12/// <content>
 13/// Rigid-frame contact generation for <see cref="FixedTriangle"/> against
 14/// centered convex shapes (cylinders, cones, etc.) that are transformed by
 15/// their own position/rotation. Complete contact queries select exact shape
 16/// features before materialization; explicitly named support-contact queries
 17/// project caller-selected supports onto the triangle. Both validate the
 18/// supplied rigid frames and retain authored contact-anchor ownership.
 19/// </content>
 20public partial struct FixedTriangle
 21{
 22    /// <summary>
 23    /// Attempts to construct a minimum-depth inclusive contact between this
 24    /// rigid triangle and a finite cylinder whose local axis is +Y.
 25    /// </summary>
 26    /// <remarks>
 27    /// The normal points from the triangle toward the cylinder. Triangle face,
 28    /// vertex/side, edge/side and admitted edge/rim stationary features share
 29    /// exact selection before final Q32.32 rounding. Anchors retain their
 30    /// authored frames, including unrepresentable absolute world points.
 31    /// Degenerate triangles and separated shapes return false.
 32    /// </remarks>
 33    public readonly bool TryGetCenteredFiniteCylinderContact(
 34        Vector3d triangleOrigin, FixedQuaternion triangleRotation,
 35        Vector3d cylinderCenter, FixedQuaternion cylinderRotation,
 36        Fixed64 cylinderHeight, Fixed64 cylinderRadius, out FixedContactAnchors contact) =>
 13437        TryGetCenteredFiniteCylinderContact(triangleOrigin, triangleRotation,
 13438            cylinderCenter, cylinderRotation, cylinderHeight, cylinderRadius, out contact, out _);
 39
 40    internal readonly bool TryGetCenteredFiniteCylinderContact(
 41        Vector3d triangleOrigin, FixedQuaternion triangleRotation,
 42        Vector3d cylinderCenter, FixedQuaternion cylinderRotation,
 43        Fixed64 cylinderHeight, Fixed64 cylinderRadius, out FixedContactAnchors contact,
 44        out bool isCapFaceContact)
 45    {
 16446        ValidateRigidTriangleFrame(triangleRotation);
 16347        ValidateRigidShapeFrame(cylinderRotation, nameof(cylinderRotation));
 16248        if (cylinderHeight <= Fixed64.Zero)
 249            throw new ArgumentOutOfRangeException(nameof(cylinderHeight));
 16050        if (cylinderRadius < Fixed64.Zero)
 151            throw new ArgumentOutOfRangeException(nameof(cylinderRadius));
 15952        return TriangleCylinderContact.TryGetContact(this, triangleOrigin, triangleRotation,
 15953            cylinderCenter, cylinderRotation, Signed192.Raw(cylinderHeight), cylinderRadius,
 15954            out contact, out isCapFaceContact);
 55    }
 56
 57    /// <summary>
 58    /// Attempts to project one centered finite-cylinder support onto this
 59    /// rigidly transformed triangle.
 60    /// </summary>
 61    public readonly bool TryGetCenteredFiniteCylinderSupportContact(
 62        Vector3d triangleOrigin,
 63        FixedQuaternion triangleRotation,
 64        Vector3d cylinderCenter,
 65        FixedQuaternion cylinderRotation,
 66        Fixed64 cylinderHeight,
 67        Fixed64 cylinderRadius,
 68        Vector3d supportDirection,
 69        Vector3d normalTriangleToCylinder,
 70        out FixedContactAnchors contact)
 71    {
 1272        ValidateRigidTriangleFrame(triangleRotation);
 1273        ValidateRigidShapeFrame(cylinderRotation, nameof(cylinderRotation));
 1274        ValidateCenteredSurfaceContact(
 1275            cylinderHeight,
 1276            cylinderRadius,
 1277            normalTriangleToCylinder,
 1278            nameof(cylinderHeight));
 979        FixedPointAnchor support =
 980            WideGeometry.GetCenteredCylinderSupportAnchor(
 981                cylinderCenter,
 982                cylinderRotation,
 983                cylinderHeight,
 984                cylinderRadius,
 985                supportDirection);
 986        return TryGetSupportContact(
 987            this,
 988            triangleOrigin,
 989            triangleRotation,
 990            support,
 991            normalTriangleToCylinder,
 992            out contact);
 93    }
 94
 95    /// <summary>
 96    /// Attempts to construct a minimum-depth inclusive contact with a finite
 97    /// cone whose local apex is at +height/2 and base disk at -height/2.
 98    /// </summary>
 99    /// <remarks>
 100    /// Exact feature admission rejects every positive gap before nearest-even
 101    /// output rounding. The normal points from triangle toward cone and both
 102    /// anchors retain their authored rigid frames. Degenerate triangles return
 103    /// false. Zero radius is an axial segment and may have positive exit depth.
 104    /// </remarks>
 105    public readonly bool TryGetCenteredFiniteConeContact(
 106        Vector3d triangleOrigin, FixedQuaternion triangleRotation,
 107        Vector3d coneCenter, FixedQuaternion coneRotation,
 108        Fixed64 coneHeight, Fixed64 coneRadius, out FixedContactAnchors contact)
 109    {
 141110        ValidateRigidTriangleFrame(triangleRotation);
 140111        ValidateRigidShapeFrame(coneRotation, nameof(coneRotation));
 139112        if (coneHeight <= Fixed64.Zero)
 2113            throw new ArgumentOutOfRangeException(nameof(coneHeight));
 137114        if (coneRadius < Fixed64.Zero)
 1115            throw new ArgumentOutOfRangeException(nameof(coneRadius));
 136116        return TriangleConeContact.TryGetContact(this, triangleOrigin, triangleRotation,
 136117            coneCenter, coneRotation, coneHeight, coneRadius, out contact);
 118    }
 119
 120    /// <summary>
 121    /// Attempts to project one centered finite-cone support onto this rigidly
 122    /// transformed triangle.
 123    /// </summary>
 124    public readonly bool TryGetCenteredFiniteConeSupportContact(
 125        Vector3d triangleOrigin,
 126        FixedQuaternion triangleRotation,
 127        Vector3d coneCenter,
 128        FixedQuaternion coneRotation,
 129        Fixed64 coneHeight,
 130        Fixed64 coneRadius,
 131        Vector3d supportDirection,
 132        Vector3d normalTriangleToCone,
 133        out FixedContactAnchors contact)
 134    {
 2135        ValidateRigidTriangleFrame(triangleRotation);
 2136        ValidateRigidShapeFrame(coneRotation, nameof(coneRotation));
 2137        ValidateCenteredSurfaceContact(
 2138            coneHeight,
 2139            coneRadius,
 2140            normalTriangleToCone,
 2141            nameof(coneHeight));
 2142        FixedPointAnchor support =
 2143            WideGeometry.GetCenteredConeSupportAnchor(
 2144                coneCenter,
 2145                coneRotation,
 2146                coneHeight,
 2147                coneRadius,
 2148                supportDirection);
 2149        return TryGetSupportContact(
 2150            this,
 2151            triangleOrigin,
 2152            triangleRotation,
 2153            support,
 2154            normalTriangleToCone,
 2155            out contact);
 156    }
 157
 158    /// <summary>
 159    /// Attempts to construct an exact contact between this rigidly transformed
 160    /// triangle and a centered capsule.
 161    /// </summary>
 162    /// <remarks>
 163    /// The returned triangle witness remains in the supplied triangle frame.
 164    /// The capsule witness remains in the supplied capsule frame so callers can
 165    /// retain stable rigid-frame feature identity independently of world pose.
 166    /// A positive-length core coinciding with the triangle retains the supplied
 167    /// fallback normal and measures its first capsule-boundary exit opposite
 168    /// that normal. This is a local witness depth, not a whole-triangle minimum
 169    /// translation. Finite endpoint and radial anchor terms remain separate.
 170    /// </remarks>
 171    public readonly bool TryGetCenteredCapsuleContact(
 172        Vector3d triangleOrigin,
 173        FixedQuaternion triangleRotation,
 174        Vector3d capsuleCenter,
 175        FixedQuaternion capsuleRotation,
 176        Fixed64 capsuleAxisLength,
 177        Fixed64 capsuleRadius,
 178        Vector3d fallbackNormal,
 179        out FixedContactAnchors contact) =>
 31180        TryGetCenteredCapsuleContact(triangleOrigin, triangleRotation, capsuleCenter,
 31181            capsuleRotation, capsuleAxisLength, capsuleRadius, fallbackNormal,
 31182            retainFeature: false, out contact, out _, out _, out _);
 183
 184    /// <summary>Retains exact feature incidence in the selected local or world contact chart.</summary>
 185    internal readonly bool TryGetCenteredCapsuleContact(
 186        Vector3d triangleOrigin, FixedQuaternion triangleRotation,
 187        Vector3d capsuleCenter, FixedQuaternion capsuleRotation,
 188        Fixed64 capsuleAxisLength, Fixed64 capsuleRadius, Vector3d fallbackNormal,
 189        out FixedContactAnchors contact, out AxisTriangleContactFeature feature,
 190        out bool chartIsTriangleLocal) =>
 43191        TryGetCenteredCapsuleContact(triangleOrigin, triangleRotation, capsuleCenter,
 43192            capsuleRotation, capsuleAxisLength, capsuleRadius, fallbackNormal,
 43193            retainFeature: true, out contact, out feature, out chartIsTriangleLocal, out _);
 194
 195    /// <summary>Contacts the exact clipped A/B segment of a degenerate A/B/B triangle.
 196    /// Clip ratios must use fewer than 268 bits, as produced by rigid edge coverage.</summary>
 197    internal readonly bool TryGetClippedCenteredCapsuleContact(
 198        Vector3d origin, FixedQuaternion rotation, Vector3d center, FixedQuaternion capsuleRotation,
 199        Fixed64 length, Fixed64 radius, Vector3d fallback, in RationalSegmentInterval interval,
 200        out FixedContactAnchors contact, out AxisTriangleContactFeature feature, out int endpoint) =>
 17201        TryGetCenteredCapsuleContact(origin, rotation, center, capsuleRotation, length, radius, fallback,
 17202            true, out contact, out feature, out _, out endpoint, interval);
 203
 204    internal readonly bool TryGetCenteredCapsuleContact(in ScaledRigidFrame3d triangleFrame,
 205        Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Fixed64 length, Fixed64 radius,
 206        Vector3d fallbackNormal, out FixedContactAnchors contact, out AxisTriangleContactFeature feature,
 207        out bool chartIsTriangleLocal)
 208    {
 33209        if (triangleFrame.IsRigid)
 2210            return TryGetCenteredCapsuleContact(triangleFrame.RigidOrigin, triangleFrame.Rotation,
 2211                capsuleCenter, capsuleRotation, length, radius, fallbackNormal, out contact, out feature, out chartIsTri
 31212        chartIsTriangleLocal = false;
 31213        ValidateRigidShapeFrame(capsuleRotation, nameof(capsuleRotation));
 31214        Vector3d axis = length == Fixed64.Zero ? Vector3d.Up : capsuleRotation.Rotate(Vector3d.Up).Normalized;
 31215        ValidateCenteredCapsuleContact(axis, length, radius, fallbackNormal);
 31216        return WideFiniteAxisIntersection.TryGetScaledCenteredCapsuleTriangleContact(this, triangleFrame,
 31217            capsuleCenter, capsuleRotation, length, radius, fallbackNormal, out contact, out feature, out _);
 218    }
 219
 220    internal readonly bool TryGetClippedCenteredCapsuleContact(in ScaledRigidFrame3d triangleFrame,
 221        Vector3d capsuleCenter, FixedQuaternion capsuleRotation, Fixed64 length, Fixed64 radius,
 222        Vector3d fallbackNormal, in RationalSegmentInterval interval, out FixedContactAnchors contact,
 223        out AxisTriangleContactFeature feature, out int endpoint, out bool chartIsTriangleLocal)
 224    {
 19225        if (triangleFrame.IsRigid && !interval.HasWideBounds)
 2226            return TryGetCenteredCapsuleContact(triangleFrame.RigidOrigin, triangleFrame.Rotation,
 2227                capsuleCenter, capsuleRotation, length, radius, fallbackNormal, true, out contact, out feature,
 2228                out chartIsTriangleLocal, out endpoint, interval);
 17229        chartIsTriangleLocal = false;
 17230        ValidateRigidShapeFrame(capsuleRotation, nameof(capsuleRotation));
 17231        Vector3d axis = length == Fixed64.Zero ? Vector3d.Up : capsuleRotation.Rotate(Vector3d.Up).Normalized;
 17232        ValidateCenteredCapsuleContact(axis, length, radius, fallbackNormal);
 17233        return WideFiniteAxisIntersection.TryGetScaledCenteredCapsuleTriangleContact(this, triangleFrame,
 17234            capsuleCenter, capsuleRotation, length, radius, fallbackNormal, out contact, out feature, out endpoint, inte
 235    }
 236    private readonly bool TryGetCenteredCapsuleContact(
 237        Vector3d triangleOrigin,
 238        FixedQuaternion triangleRotation,
 239        Vector3d capsuleCenter,
 240        FixedQuaternion capsuleRotation,
 241        Fixed64 capsuleAxisLength,
 242        Fixed64 capsuleRadius,
 243        Vector3d fallbackNormal,
 244        bool retainFeature,
 245        out FixedContactAnchors contact,
 246        out AxisTriangleContactFeature feature,
 247        out bool chartIsTriangleLocal,
 248        out int intervalEndpoint,
 249        in RationalSegmentInterval interval = default)
 250    {
 93251        intervalEndpoint = 0;
 93252        feature = default;
 93253        chartIsTriangleLocal = false;
 93254        ValidateRigidTriangleFrame(triangleRotation);
 92255        ValidateRigidShapeFrame(capsuleRotation, nameof(capsuleRotation));
 256        // A zero core is one point; orientation affects its anchor frame only.
 92257        Vector3d capsuleAxisDirection = capsuleAxisLength == Fixed64.Zero
 92258            ? Vector3d.Up : capsuleRotation.Rotate(Vector3d.Up).Normalized;
 92259        ValidateCenteredCapsuleContact(
 92260            capsuleAxisDirection,
 92261            capsuleAxisLength,
 92262            capsuleRadius,
 92263            fallbackNormal);
 264
 92265        var capsuleCenterAnchor = new FixedPointAnchor(
 92266            capsuleCenter,
 92267            FixedQuaternion.Identity,
 92268            Vector3d.Zero);
 92269        FixedQuaternion inverseTriangleRotation =
 92270            triangleRotation.Inverse();
 92271        if (capsuleCenterAnchor.TryGetLocalPointIn(
 92272                triangleOrigin,
 92273                triangleRotation,
 92274                out Vector3d localCapsuleCenter))
 275        {
 87276            chartIsTriangleLocal = true;
 87277            return TryGetCenteredCapsuleContactInChart(
 87278                this,
 87279                localCapsuleCenter,
 87280                capsuleAxisLength == Fixed64.Zero ? Vector3d.Up : inverseTriangleRotation.Rotate(capsuleAxisDirection),
 87281                capsuleAxisLength,
 87282                capsuleRadius,
 87283                inverseTriangleRotation.Rotate(fallbackNormal),
 87284                triangleOrigin,
 87285                triangleRotation,
 87286                capsuleCenter,
 87287                capsuleRotation,
 87288                chartIsTriangleLocal: true,
 87289                retainFeature, out contact, out feature, out intervalEndpoint, interval);
 290        }
 291
 5292        FixedPointAnchor first = new(
 5293            triangleOrigin,
 5294            triangleRotation,
 5295            A);
 5296        FixedPointAnchor second = new(
 5297            triangleOrigin,
 5298            triangleRotation,
 5299            B);
 5300        FixedPointAnchor third = new(
 5301            triangleOrigin,
 5302            triangleRotation,
 5303            C);
 5304        bool triangleRepresentable =
 5305            first.TryGetPoint(out Vector3d worldFirst)
 5306            & second.TryGetPoint(out Vector3d worldSecond)
 5307            & third.TryGetPoint(out Vector3d worldThird);
 5308        if (!triangleRepresentable)
 309        {
 2310            contact = default;
 2311            return false;
 312        }
 313
 3314        return TryGetCenteredCapsuleContactInChart(
 3315            new FixedTriangle(worldFirst, worldSecond, worldThird),
 3316            capsuleCenter,
 3317            capsuleAxisDirection,
 3318            capsuleAxisLength,
 3319            capsuleRadius,
 3320            fallbackNormal,
 3321            triangleOrigin,
 3322            triangleRotation,
 3323            capsuleCenter,
 3324            capsuleRotation,
 3325            chartIsTriangleLocal: false,
 3326            retainFeature, out contact, out feature, out intervalEndpoint, interval);
 327    }
 328
 329    /// <summary>
 330    /// Attempts to construct an exact contact between this rigidly transformed
 331    /// triangle and a sphere.
 332    /// </summary>
 333    /// <remarks>
 334    /// The relation selects whichever exact coordinate chart can represent the
 335    /// interacting features. The returned triangle witness always remains in
 336    /// the supplied triangle frame.
 337    /// </remarks>
 338    public readonly bool TryGetSphereContact(
 339        Vector3d triangleOrigin,
 340        FixedQuaternion triangleRotation,
 341        Vector3d sphereCenter,
 342        FixedQuaternion sphereRotation,
 343        Fixed64 sphereRadius,
 344        out FixedContactAnchors contact)
 345    {
 9346        ValidateRigidTriangleFrame(triangleRotation);
 8347        ValidateRigidShapeFrame(sphereRotation, nameof(sphereRotation));
 7348        if (sphereRadius < Fixed64.Zero)
 1349            throw new ArgumentOutOfRangeException(nameof(sphereRadius));
 350
 6351        var sphereCenterAnchor = new FixedPointAnchor(
 6352            sphereCenter,
 6353            FixedQuaternion.Identity,
 6354            Vector3d.Zero);
 6355        if (sphereCenterAnchor.TryGetLocalPointIn(
 6356                triangleOrigin,
 6357                triangleRotation,
 6358                out Vector3d localSphereCenter))
 359        {
 4360            return TryGetSphereContactInChart(
 4361                this,
 4362                localSphereCenter,
 4363                sphereCenter,
 4364                sphereRotation,
 4365                sphereRadius,
 4366                triangleOrigin,
 4367                triangleRotation,
 4368                chartIsTriangleLocal: true,
 4369                out contact);
 370        }
 371
 2372        FixedPointAnchor first = new(
 2373            triangleOrigin,
 2374            triangleRotation,
 2375            A);
 2376        FixedPointAnchor second = new(
 2377            triangleOrigin,
 2378            triangleRotation,
 2379            B);
 2380        FixedPointAnchor third = new(
 2381            triangleOrigin,
 2382            triangleRotation,
 2383            C);
 2384        bool triangleRepresentable =
 2385            first.TryGetPoint(out Vector3d worldFirst)
 2386            & second.TryGetPoint(out Vector3d worldSecond)
 2387            & third.TryGetPoint(out Vector3d worldThird);
 2388        if (!triangleRepresentable)
 389        {
 1390            contact = default;
 1391            return false;
 392        }
 393
 1394        return TryGetSphereContactInChart(
 1395            new FixedTriangle(worldFirst, worldSecond, worldThird),
 1396            sphereCenter,
 1397            sphereCenter,
 1398            sphereRotation,
 1399            sphereRadius,
 1400            triangleOrigin,
 1401            triangleRotation,
 1402            chartIsTriangleLocal: false,
 1403            out contact);
 404    }
 405
 406    private static void ValidateCenteredSurfaceContact(
 407        Fixed64 height,
 408        Fixed64 radius,
 409        Vector3d normal,
 410        string heightParameterName)
 411    {
 14412        if (height <= Fixed64.Zero)
 1413            throw new ArgumentOutOfRangeException(heightParameterName);
 13414        if (radius < Fixed64.Zero)
 1415            throw new ArgumentOutOfRangeException(nameof(radius));
 12416        if (!normal.IsNormalized())
 1417            throw new ArgumentException("Triangle contact normal must be normalized.", nameof(normal));
 11418    }
 419
 420    private static bool TryGetSphereContactInChart(
 421        FixedTriangle triangle,
 422        Vector3d chartSphereCenter,
 423        Vector3d worldSphereCenter,
 424        FixedQuaternion sphereRotation,
 425        Fixed64 sphereRadius,
 426        Vector3d triangleOrigin,
 427        FixedQuaternion triangleRotation,
 428        bool chartIsTriangleLocal,
 429        out FixedContactAnchors contact)
 430    {
 5431        Vector3d pointOnTriangle = triangle.ClosestPoint(chartSphereCenter);
 5432        if (!Vector3d.TryGetDistance(
 5433                chartSphereCenter,
 5434                pointOnTriangle,
 5435                out Fixed64 distance)
 5436            || distance > sphereRadius)
 437        {
 1438            contact = default;
 1439            return false;
 440        }
 441
 442        Vector3d chartNormal;
 4443        if (distance > Fixed64.Epsilon)
 444        {
 2445            _ = Vector3d.TrySubtract(
 2446                chartSphereCenter,
 2447                pointOnTriangle,
 2448                out Vector3d separation);
 2449            chartNormal = separation / distance;
 450        }
 451        else
 452        {
 2453            chartNormal = triangle.Normal;
 2454            if (Vector3d.CompareProjection(
 2455                    chartSphereCenter,
 2456                    triangle.Centroid,
 2457                    chartNormal) < 0)
 458            {
 1459                chartNormal = -chartNormal;
 460            }
 461        }
 462
 4463        Vector3d worldNormal = chartIsTriangleLocal
 4464            ? triangleRotation.Rotate(chartNormal).Normalized
 4465            : chartNormal;
 4466        Vector3d triangleLocalPoint = pointOnTriangle;
 4467        if (!chartIsTriangleLocal)
 468        {
 469            // A closest point of the materialized world triangle is a convex
 470            // combination of the representable source-local vertices.
 1471            _ = new FixedPointAnchor(
 1472                    pointOnTriangle,
 1473                    FixedQuaternion.Identity,
 1474                    Vector3d.Zero)
 1475                .TryGetLocalPointIn(
 1476                    triangleOrigin,
 1477                    triangleRotation,
 1478                    out triangleLocalPoint);
 479        }
 480
 4481        contact = new FixedContactAnchors(
 4482            new FixedPointAnchor(
 4483                triangleOrigin,
 4484                triangleRotation,
 4485                triangleLocalPoint),
 4486            new FixedPointAnchor(
 4487                worldSphereCenter,
 4488                sphereRotation,
 4489                sphereRotation.Inverse().Rotate(-worldNormal) * sphereRadius),
 4490            worldNormal,
 4491            sphereRadius - distance,
 4492            depthIsClamped: false);
 4493        return true;
 494    }
 495
 496    private static bool TryGetCenteredCapsuleContactInChart(
 497        FixedTriangle triangle,
 498        Vector3d chartCapsuleCenter,
 499        Vector3d chartCapsuleAxis,
 500        Fixed64 capsuleAxisLength,
 501        Fixed64 capsuleRadius,
 502        Vector3d chartFallbackNormal,
 503        Vector3d triangleOrigin,
 504        FixedQuaternion triangleRotation,
 505        Vector3d worldCapsuleCenter,
 506        FixedQuaternion capsuleRotation,
 507        bool chartIsTriangleLocal,
 508        bool retainFeature,
 509        out FixedContactAnchors contact,
 510        out AxisTriangleContactFeature feature,
 511        out int intervalEndpoint,
 512        in RationalSegmentInterval interval = default)
 513    {
 90514        if (!WideFiniteAxisIntersection
 90515            .TryGetCenteredCapsuleTriangleLocalContact(
 90516                triangle,
 90517                chartCapsuleCenter,
 90518                chartCapsuleAxis,
 90519                capsuleAxisLength,
 90520                capsuleRadius,
 90521                chartFallbackNormal,
 90522                chartIsTriangleLocal ? triangleRotation : FixedQuaternion.Identity,
 90523                capsuleRotation,
 90524                out Vector3d pointOnTriangle,
 90525                out Fixed64 axisParameter,
 90526                out Vector3d chartNormal,
 90527                out Fixed64 depth,
 90528                out bool depthIsClamped,
 90529                retainFeature, out feature, out Vector3d rayRadialOffset,
 90530                out int rayEndpointSign, out bool hasBoundaryRay, out intervalEndpoint, interval))
 531        {
 4532            contact = default;
 4533            return false;
 534        }
 535
 86536        Vector3d worldNormal = chartIsTriangleLocal
 86537            ? triangleRotation.Rotate(chartNormal).Normalized
 86538            : chartNormal;
 86539        Vector3d triangleLocalPoint = pointOnTriangle;
 86540        if (!chartIsTriangleLocal)
 541        {
 3542            _ = new FixedPointAnchor(
 3543                    pointOnTriangle,
 3544                    FixedQuaternion.Identity,
 3545                    Vector3d.Zero)
 3546                .TryGetLocalPointIn(
 3547                    triangleOrigin,
 3548                    triangleRotation,
 3549                    out triangleLocalPoint);
 550        }
 551
 86552        Vector3d capsuleLocalNormal =
 86553            capsuleRotation.Inverse().Rotate(worldNormal);
 86554        Vector3d capsuleRadialOffset = hasBoundaryRay
 86555            ? rayRadialOffset : -capsuleLocalNormal * capsuleRadius;
 86556        Vector3d capsuleCorePoint = new(Fixed64.Zero, axisParameter, Fixed64.Zero);
 86557        FixedPointAnchorTerm3d exactEndpoint = hasBoundaryRay && rayEndpointSign != 0
 86558            ? FixedPointAnchorTerm3d.CreateCenteredAxisSupport(Vector3d.Up,
 86559                rayEndpointSign > 0 ? capsuleAxisLength : -capsuleAxisLength,
 86560                Vector3d.Zero, Fixed64.Zero, capsuleCorePoint, Vector3d.Zero)
 86561            : default;
 86562        contact = new FixedContactAnchors(
 86563            new FixedPointAnchor(
 86564                triangleOrigin,
 86565                triangleRotation,
 86566                triangleLocalPoint),
 86567            new FixedPointAnchor(
 86568                worldCapsuleCenter,
 86569                capsuleRotation,
 86570                capsuleCorePoint,
 86571                capsuleRadialOffset,
 86572                exactEndpoint),
 86573            worldNormal,
 86574            depth,
 86575            depthIsClamped);
 86576        return true;
 577    }
 578
 579    private static bool TryGetSupportContact(
 580        FixedTriangle triangle,
 581        Vector3d triangleOrigin,
 582        FixedQuaternion triangleRotation,
 583        in FixedPointAnchor support,
 584        Vector3d worldNormal,
 585        out FixedContactAnchors contact)
 586    {
 11587        if (!support.TryGetLocalPointIn(
 11588                triangleOrigin,
 11589                triangleRotation,
 11590                out Vector3d localSupport))
 591        {
 1592            contact = default;
 1593            return false;
 594        }
 595
 10596        Vector3d localNormal =
 10597            triangleRotation.Inverse().Rotate(worldNormal);
 10598        if (!WideFiniteAxisIntersection.TryGetTriangleSupportPointContact(
 10599                triangle,
 10600                localSupport,
 10601                localNormal,
 10602                out Vector3d pointOnTriangle,
 10603                out Fixed64 depth,
 10604                out bool depthIsClamped))
 605        {
 5606            contact = default;
 5607            return false;
 608        }
 609
 5610        contact = new FixedContactAnchors(
 5611            new FixedPointAnchor(
 5612                triangleOrigin,
 5613                triangleRotation,
 5614                pointOnTriangle),
 5615            support,
 5616            worldNormal,
 5617            depth,
 5618            depthIsClamped);
 5619        return true;
 620    }
 621
 622    private static void ValidateRigidTriangleFrame(
 623        FixedQuaternion triangleRotation)
 624    {
 925625        if (!triangleRotation.IsNormalized())
 626        {
 6627            throw new ArgumentException(
 6628                "The triangle rotation must be normalized.",
 6629                nameof(triangleRotation));
 630        }
 919631    }
 632
 633    private static void ValidateRigidShapeFrame(
 634        FixedQuaternion rotation,
 635        string parameterName)
 636    {
 465637        if (!rotation.IsNormalized())
 638        {
 3639            throw new ArgumentException(
 3640                "The shape rotation must be normalized.",
 3641                parameterName);
 642        }
 462643    }
 644}

Methods/Properties

.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
get_UnnormalizedNormal()
get_Normal()
get_Area()
get_Bounds()
get_Centroid()
get_IsDegenerate()
GetVertex(System.Int32)
GetEdge(System.Int32)
GetPoint(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryGetProjectedBarycentricWeights(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
GetClosestPointAnchor(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Geometry.FixedPointAnchor&)
ContainsProjection(FixedMathSharp.Vector3d)
Contains(FixedMathSharp.Vector3d)
ClosestPoint(FixedMathSharp.Vector3d)
TryGetClosestPointBarycentrics(FixedMathSharp.Vector3d,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,FixedMathSharp.Signed320&,System.Int32&)
DistanceSquared(FixedMathSharp.Vector3d)
Deconstruct(FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
op_Equality(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Geometry.FixedTriangle)
op_Inequality(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Geometry.FixedTriangle)
Equals(FixedMathSharp.Geometry.FixedTriangle)
Equals(System.Object)
GetHashCode()
ClosestPointOnEdges(FixedMathSharp.Vector3d)
TrySetCloserPoint(FixedMathSharp.Geometry.FixedSegment,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&)
GetExactNormal(FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed320&)
GetExactNormalComponents(FixedMathSharp.Signed192&,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
GetDifferenceDot(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
ComponentMin(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
ComponentMax(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
TryGetCircleSlabContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredCapsuleSlabContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
ValidateCapsuleSlab(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryGetClosestPointsToCenteredAxis(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&)
DoesCenteredCapsuleOverlap(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryGetCenteredCapsuleContact(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
ValidateCenteredCapsuleContact(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d)
ValidateCenteredAxis(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
TryGetFiniteConeIntersectionMinimumAxialPoint(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d&)
TryGetFiniteConeIntersectionMinimumAxialPoint(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedPointAnchor&)
ValidateFiniteCone(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.String,System.String)
TryGetFiniteSlabProjectedCircleContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedPointAnchor&)
TryGetFiniteSlabProjectedCircleSweep(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Geometry.FixedPointAnchor&)
TryGetFiniteSlabProjectedCircleSweep(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Geometry.FixedPointAnchor&)
TryGetContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredFiniteCylinderContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredFiniteCylinderContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&,System.Boolean&)
TryGetCenteredFiniteCylinderSupportContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredFiniteConeContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredFiniteConeSupportContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredCapsuleContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredCapsuleContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.AxisTriangleContactFeature&,System.Boolean&)
TryGetClippedCenteredCapsuleContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.AxisTriangleContactFeature&,System.Int32&)
TryGetCenteredCapsuleContact(FixedMathSharp.Geometry.ScaledRigidFrame3d&,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.AxisTriangleContactFeature&,System.Boolean&)
TryGetClippedCenteredCapsuleContact(FixedMathSharp.Geometry.ScaledRigidFrame3d&,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.RationalSegmentInterval&,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.AxisTriangleContactFeature&,System.Int32&,System.Boolean&)
TryGetCenteredCapsuleContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,System.Boolean,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.AxisTriangleContactFeature&,System.Boolean&,System.Int32&,FixedMathSharp.Geometry.RationalSegmentInterval&)
TryGetSphereContact(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Geometry.FixedContactAnchors&)
ValidateCenteredSurfaceContact(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,System.String)
TryGetSphereContactInChart(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,System.Boolean,FixedMathSharp.Geometry.FixedContactAnchors&)
TryGetCenteredCapsuleContactInChart(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,System.Boolean,System.Boolean,FixedMathSharp.Geometry.FixedContactAnchors&,FixedMathSharp.Geometry.AxisTriangleContactFeature&,System.Int32&,FixedMathSharp.Geometry.RationalSegmentInterval&)
TryGetSupportContact(FixedMathSharp.Geometry.FixedTriangle,FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Geometry.FixedPointAnchor&,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedContactAnchors&)
ValidateRigidTriangleFrame(FixedMathSharp.FixedQuaternion)
ValidateRigidShapeFrame(FixedMathSharp.FixedQuaternion,System.String)