< Summary

Information
Class: FixedMathSharp.Fixed3x3
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Matrices/Fixed3x3.cs
Line coverage
100%
Covered lines: 231
Uncovered lines: 0
Coverable lines: 231
Total lines: 796
Line coverage: 100%
Branch coverage
93%
Covered branches: 71
Total branches: 76
Branch coverage: 93.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
get_Item(...)100%1212100%
set_Item(...)100%1212100%
NormalizeInPlace()100%11100%
ResetScaleToIdentity()100%11100%
GetDeterminant()100%11100%
InvertDiagonal()100%66100%
CreateRotationX(...)100%11100%
CreateRotationY(...)100%11100%
CreateRotationZ(...)100%11100%
CreateShear(...)100%11100%
CreateScale(...)100%11100%
CreateScale(...)100%11100%
CreateBarycentricProductSums(...)100%11100%
GetNormalized(...)100%11100%
ResetScaleToIdentity(...)100%11100%
SetLossyScale(...)100%11100%
SetLossyScale(...)100%11100%
SetScale(...)100%11100%
SetGlobalScale(...)100%11100%
ExtractScale(...)100%11100%
ExtractLossyScale(...)100%11100%
Lerp(...)100%11100%
Transpose(...)100%11100%
Invert(...)100%22100%
TransformDirection(...)100%11100%
InverseTransformDirection(...)100%44100%
op_Subtraction(...)100%11100%
op_Addition(...)100%11100%
op_UnaryNegation(...)100%11100%
op_Multiply(...)100%11100%
op_Multiply(...)100%11100%
op_Multiply(...)100%11100%
op_Division(...)100%11100%
op_Equality(...)100%11100%
op_Inequality(...)100%11100%
Equals(...)100%1616100%
Equals(...)100%22100%
GetHashCode()100%11100%
ToString()100%11100%
ToString(...)100%11100%
TryFormat(...)64.28%1414100%
AppendRow(...)100%88100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Matrices/Fixed3x3.cs

#LineLine coverage
 1//=======================================================================
 2// Fixed3x3.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.Globalization;
 11using System.Runtime.CompilerServices;
 12using System.Text.Json.Serialization;
 13
 14namespace FixedMathSharp;
 15
 16/// <summary>
 17/// Represents a 3x3 matrix used for linear transformations in 2D and 3D space, such as rotation, scaling, and shearing.
 18/// </summary>
 19/// <remarks>
 20/// A 3x3 matrix handles only linear transformations and is typically used when translation is not needed.
 21/// It operates on directions, orientations, and vectors within a given space without affecting position.
 22/// This matrix is more lightweight compared to a 4x4 matrix, making it ideal when translation and perspective are unnec
 23///
 24/// Use Cases:
 25/// - Rotating or scaling objects around the origin in 2D and 3D space.
 26/// - Transforming vectors and normals (e.g., in lighting calculations).
 27/// - Used in physics engines for inertia tensors or to represent local orientations.
 28/// - Useful when optimizing transformations, as it omits the overhead of translation and perspective.
 29/// </remarks>
 30[Serializable]
 31[MemoryPackable]
 32public partial struct Fixed3x3 : IEquatable<Fixed3x3>, IFormattable
 33#if NET8_0_OR_GREATER
 34    , ISpanFormattable
 35#endif
 36{
 37    #region Static Readonly
 38
 39    /// <summary>
 40    /// Returns the identity matrix (no scaling, rotation, or translation).
 41    /// </summary>
 142    public static readonly Fixed3x3 Identity = new(Vector3d.FromDouble(1f, 0f, 0f), Vector3d.FromDouble(0f, 1f, 0f), Vec
 43
 44    /// <summary>
 45    /// Returns a matrix with all elements set to zero.
 46    /// </summary>
 147    public static readonly Fixed3x3 Zero = new(Vector3d.FromDouble(0f, 0f, 0f), Vector3d.FromDouble(0f, 0f, 0f), Vector3
 48
 49    #endregion
 50
 51    #region Fields
 52
 53    // First row
 54
 55    /// <summary>
 56    /// Represents the element in the first row and first column of the matrix.
 57    /// </summary>
 58    [JsonInclude]
 59    [MemoryPackOrder(0)]
 60    public Fixed64 M11;
 61    /// <summary>
 62    /// Represents the element in the first row and second column of the matrix.
 63    /// </summary>
 64    [JsonInclude]
 65    [MemoryPackOrder(1)]
 66    public Fixed64 M12;
 67    /// <summary>
 68    /// Represents the element in the first row and third column of the matrix.
 69    /// </summary>
 70    [JsonInclude]
 71    [MemoryPackOrder(2)]
 72    public Fixed64 M13;
 73
 74    // Second Row
 75
 76    /// <summary>
 77    /// Represents the element in the second row and first column of the matrix.
 78    /// </summary>
 79    [JsonInclude]
 80    [MemoryPackOrder(3)]
 81    public Fixed64 M21;
 82    /// <summary>
 83    /// Represents the element in the second row and second column of the matrix.
 84    /// </summary>
 85    [JsonInclude]
 86    [MemoryPackOrder(4)]
 87    public Fixed64 M22;
 88    /// <summary>
 89    /// Represents the element in the second row and third column of the matrix.
 90    /// </summary>
 91    [JsonInclude]
 92    [MemoryPackOrder(5)]
 93    public Fixed64 M23;
 94
 95    // Third Row
 96
 97    /// <summary>
 98    /// Represents the element in the third row and first column of the matrix.
 99    /// </summary>
 100    [JsonInclude]
 101    [MemoryPackOrder(6)]
 102    public Fixed64 M31;
 103    /// <summary>
 104    /// Represents the element in the third row and second column of the matrix.
 105    /// </summary>
 106    [JsonInclude]
 107    [MemoryPackOrder(7)]
 108    public Fixed64 M32;
 109    /// <summary>
 110    /// Represents the element in the third row and third column of the matrix.
 111    /// </summary>
 112    [JsonInclude]
 113    [MemoryPackOrder(8)]
 114    public Fixed64 M33;
 115
 116    #endregion
 117
 118    #region Constructors
 119
 120    /// <summary>
 121    /// Initializes a new FixedMatrix3x3 with the specified elements.
 122    /// </summary>
 123    public Fixed3x3(
 124        Fixed64 m11, Fixed64 m12, Fixed64 m13,
 125        Fixed64 m21, Fixed64 m22, Fixed64 m23,
 126        Fixed64 m31, Fixed64 m32, Fixed64 m33
 127    )
 128    {
 486129        M11 = m11; M12 = m12; M13 = m13;
 486130        M21 = m21; M22 = m22; M23 = m23;
 486131        M31 = m31; M32 = m32; M33 = m33;
 162132    }
 133
 134    /// <summary>
 135    /// Initializes a new FixedMatrix3x3 using three Vector3d values representing the rows.
 136    /// </summary>
 137    public Fixed3x3(
 138        Vector3d m11_m12_m13,
 139        Vector3d m21_m22_m23,
 140        Vector3d m31_m32_m33
 4141    ) : this(
 4142        m11_m12_m13.X,
 4143        m11_m12_m13.Y,
 4144        m11_m12_m13.Z,
 4145        m21_m22_m23.X,
 4146        m21_m22_m23.Y,
 4147        m21_m22_m23.Z,
 4148        m31_m32_m33.X,
 4149        m31_m32_m33.Y,
 4150        m31_m32_m33.Z)
 4151    { }
 152
 153    #endregion
 154
 155    #region Properties
 156
 157    /// <summary>
 158    /// Gets or sets the matrix element at the specified index.
 159    /// </summary>
 160    /// <remarks>
 161    /// The mapping between indices and matrix elements is non-sequential.
 162    /// Ensure that the index corresponds to a valid matrix element.
 163    /// </remarks>
 164    /// <param name="index">The zero-based index of the matrix element to get or set. Valid values are 0, 1, 2, 4, 5, 6,
 165    /// <returns>The matrix element at the specified index.</returns>
 166    /// <exception cref="IndexOutOfRangeException">Thrown when the specified index is not one of the valid matrix elemen
 167    [JsonIgnore]
 168    [MemoryPackIgnore]
 169    public Fixed64 this[int index]
 170    {
 171        get
 172        {
 13173            return index switch
 13174            {
 1175                0 => M11,
 1176                1 => M21,
 1177                2 => M31,
 1178                4 => M12,
 1179                5 => M22,
 1180                6 => M32,
 1181                8 => M13,
 1182                9 => M23,
 1183                10 => M33,
 4184                _ => throw new IndexOutOfRangeException("Invalid matrix index!"),
 13185            };
 186        }
 187        set
 188        {
 189            switch (index)
 190            {
 191                case 0:
 1192                    M11 = value;
 1193                    break;
 194                case 1:
 1195                    M21 = value;
 1196                    break;
 197                case 2:
 1198                    M31 = value;
 1199                    break;
 200                case 4:
 1201                    M12 = value;
 1202                    break;
 203                case 5:
 1204                    M22 = value;
 1205                    break;
 206                case 6:
 1207                    M32 = value;
 1208                    break;
 209                case 8:
 1210                    M13 = value;
 1211                    break;
 212                case 9:
 1213                    M23 = value;
 1214                    break;
 215                case 10:
 1216                    M33 = value;
 1217                    break;
 218                default:
 4219                    throw new IndexOutOfRangeException("Invalid matrix index!");
 220            }
 221        }
 222    }
 223
 224    #endregion
 225
 226    #region Methods (Instance)
 227
 228    /// <inheritdoc cref="GetNormalized(Fixed3x3)" />
 3229    public Fixed3x3 NormalizeInPlace() => this = GetNormalized(this);
 230
 231    /// <inheritdoc cref="ResetScaleToIdentity(Fixed3x3)" />
 232    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2233    public Fixed3x3 ResetScaleToIdentity() => this = ResetScaleToIdentity(this);
 234
 235    /// <summary>
 236    /// Calculates the determinant of a 3x3 matrix.
 237    /// </summary>
 238    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 239    public Fixed64 GetDeterminant() =>
 7240         M11 * (M22 * M33 - M23 * M32) -
 7241         M12 * (M21 * M33 - M23 * M31) +
 7242         M13 * (M21 * M32 - M22 * M31);
 243
 244    /// <summary>
 245    /// Inverts the diagonal elements of the matrix.
 246    /// </summary>
 247    /// <remarks>
 248    /// protects against the case where you would have an infinite value on the diagonal, which would cause problems in 
 249    /// If m00 or m22 are zero, handle that as a special case and manually set the inverse to zero,
 250    /// since for a theoretical object with no inertia along those axes, it would be impossible to impart a rotation in 
 251    ///
 252    ///  bear in mind that having a zero on the inertia tensor's diagonal isn't generally valid for real,
 253    ///  3-dimensional objects (unless they are "infinitely thin" along one axis),
 254    ///  so if you end up with such a tensor, it's a sign that something else might be wrong in your setup.
 255    /// </remarks>
 256    public Fixed3x3 InvertDiagonal()
 257    {
 258        try
 259        {
 3260            if (M22 == Fixed64.Zero)
 1261                throw new ArgumentException("Cannot invert a diagonal matrix with zero elements on the diagonal.");
 2262        }
 1263        catch (ArgumentException)
 264        {
 1265            return this;
 266        }
 267
 2268        return new Fixed3x3(
 2269            M11 != Fixed64.Zero ? Fixed64.One / M11 : Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 2270            Fixed64.Zero, Fixed64.One / M22, Fixed64.Zero,
 2271            Fixed64.Zero, Fixed64.Zero, M33 != Fixed64.Zero ? Fixed64.One / M33 : Fixed64.Zero
 2272        );
 1273    }
 274
 275    #endregion
 276
 277    #region Static Matrix Generators and Transformations
 278
 279    /// <summary>
 280    /// Creates a 3x3 matrix representing a rotation around the X-axis.
 281    /// </summary>
 282    /// <param name="angle">The angle of rotation in radians.</param>
 283    /// <returns>A 3x3 rotation matrix.</returns>
 284    public static Fixed3x3 CreateRotationX(Fixed64 angle)
 285    {
 10286        Fixed64 cos = FixedMath.Cos(angle);
 10287        Fixed64 sin = FixedMath.Sin(angle);
 288
 10289        return new Fixed3x3(
 10290            Fixed64.One, Fixed64.Zero, Fixed64.Zero,
 10291            Fixed64.Zero, cos, sin,
 10292            Fixed64.Zero, -sin, cos
 10293        );
 294    }
 295
 296    /// <summary>
 297    /// Creates a 3x3 matrix representing a rotation around the Y-axis.
 298    /// </summary>
 299    /// <param name="angle">The angle of rotation in radians.</param>
 300    /// <returns>A 3x3 rotation matrix.</returns>
 301    public static Fixed3x3 CreateRotationY(Fixed64 angle)
 302    {
 11303        Fixed64 cos = FixedMath.Cos(angle);
 11304        Fixed64 sin = FixedMath.Sin(angle);
 305
 11306        return new Fixed3x3(
 11307            cos, Fixed64.Zero, -sin,
 11308            Fixed64.Zero, Fixed64.One, Fixed64.Zero,
 11309            sin, Fixed64.Zero, cos
 11310        );
 311    }
 312
 313    /// <summary>
 314    /// Creates a 3x3 matrix representing a rotation around the Z-axis.
 315    /// </summary>
 316    /// <param name="angle">The angle of rotation in radians.</param>
 317    /// <returns>A 3x3 rotation matrix.</returns>
 318    public static Fixed3x3 CreateRotationZ(Fixed64 angle)
 319    {
 7320        Fixed64 cos = FixedMath.Cos(angle);
 7321        Fixed64 sin = FixedMath.Sin(angle);
 322
 7323        return new Fixed3x3(
 7324            cos, sin, Fixed64.Zero,
 7325            -sin, cos, Fixed64.Zero,
 7326            Fixed64.Zero, Fixed64.Zero, Fixed64.One
 7327        );
 328    }
 329
 330    /// <summary>
 331    /// Creates a 3x3 shear matrix.
 332    /// </summary>
 333    /// <param name="shX">Shear factor along the X-axis.</param>
 334    /// <param name="shY">Shear factor along the Y-axis.</param>
 335    /// <param name="shZ">Shear factor along the Z-axis.</param>
 336    /// <returns>A 3x3 shear matrix.</returns>
 337    public static Fixed3x3 CreateShear(Fixed64 shX, Fixed64 shY, Fixed64 shZ) =>
 1338         new(Fixed64.One, shX, shY,
 1339            shX, Fixed64.One, shZ,
 1340            shY, shZ, Fixed64.One);
 341
 342    /// <summary>
 343    /// Creates a scaling matrix that applies a uniform or non-uniform scale transformation.
 344    /// </summary>
 345    /// <param name="scale">The scale factors along the X, Y, and Z axes.</param>
 346    /// <returns>A 3x3 scaling matrix.</returns>
 347    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 348    public static Fixed3x3 CreateScale(Vector3d scale) =>
 11349        new(scale.X, Fixed64.Zero, Fixed64.Zero,
 11350            Fixed64.Zero, scale.Y, Fixed64.Zero,
 11351            Fixed64.Zero, Fixed64.Zero, scale.Z);
 352
 353    /// <summary>
 354    /// Creates a uniform scaling matrix with the same scale factor on all axes.
 355    /// </summary>
 356    /// <param name="scaleFactor">The uniform scale factor.</param>
 357    /// <returns>A 3x3 scaling matrix with uniform scaling.</returns>
 358    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 359    public static Fixed3x3 CreateScale(Fixed64 scaleFactor) =>
 1360        CreateScale(new Vector3d(scaleFactor, scaleFactor, scaleFactor));
 361
 362    /// <summary>
 363    /// Creates a symmetric matrix containing second-order barycentric product sums for three vectors.
 364    /// </summary>
 365    /// <remarks>
 366    /// The diagonal contains squared component sums, and the off-diagonal elements contain cross-component sums.
 367    /// </remarks>
 368    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 369    public static Fixed3x3 CreateBarycentricProductSums(Vector3d a, Vector3d b, Vector3d c)
 370    {
 1371        Fixed64 xx = FixedMath.SumSquaredBarycentricProducts(a.X, b.X, c.X);
 1372        Fixed64 yy = FixedMath.SumSquaredBarycentricProducts(a.Y, b.Y, c.Y);
 1373        Fixed64 zz = FixedMath.SumSquaredBarycentricProducts(a.Z, b.Z, c.Z);
 1374        Fixed64 xy = FixedMath.SumBarycentricProducts(a.X, b.X, c.X, a.Y, b.Y, c.Y);
 1375        Fixed64 xz = FixedMath.SumBarycentricProducts(a.X, b.X, c.X, a.Z, b.Z, c.Z);
 1376        Fixed64 yz = FixedMath.SumBarycentricProducts(a.Y, b.Y, c.Y, a.Z, b.Z, c.Z);
 377
 1378        return new Fixed3x3(
 1379            xx, xy, xz,
 1380            xy, yy, yz,
 1381            xz, yz, zz);
 382    }
 383
 384    /// <summary>
 385    /// Normalizes the basis vectors of a 3x3 matrix to ensure they are orthogonal and unit length.
 386    /// </summary>
 387    /// <remarks>
 388    /// This method recalculates and normalizes the X, Y, and Z basis vectors of the matrix to avoid numerical drift
 389    /// that can occur after multiple transformations. It also ensures that the Z-axis is recomputed to maintain
 390    /// orthogonality by taking the cross-product of the normalized X and Y axes.
 391    ///
 392    /// Use Cases:
 393    /// - Ensuring stability and correctness after repeated transformations involving rotation and scaling.
 394    /// - Useful in physics calculations where orthogonal matrices are required (e.g., inertia tensors or rotations).
 395    /// </remarks>
 396    public static Fixed3x3 GetNormalized(Fixed3x3 matrix)
 397    {
 3398        var x = new Vector3d(matrix.M11, matrix.M12, matrix.M13).NormalizeInPlace();
 3399        var y = new Vector3d(matrix.M21, matrix.M22, matrix.M23).NormalizeInPlace();
 3400        var z = Vector3d.Cross(x, y).NormalizeInPlace();
 401
 9402        matrix.M11 = x.X; matrix.M12 = x.Y; matrix.M13 = x.Z;
 9403        matrix.M21 = y.X; matrix.M22 = y.Y; matrix.M23 = y.Z;
 9404        matrix.M31 = z.X; matrix.M32 = z.Y; matrix.M33 = z.Z;
 405
 3406        return matrix;
 407    }
 408
 409    /// <summary>
 410    /// Resets the scaling part of the matrix to identity (1,1,1).
 411    /// </summary>
 412    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 413    public static Fixed3x3 ResetScaleToIdentity(Fixed3x3 matrix)
 414    {
 2415        matrix.M11 = Fixed64.One;  // Reset scale on X-axis
 2416        matrix.M22 = Fixed64.One;  // Reset scale on Y-axis
 2417        matrix.M33 = Fixed64.One;  // Reset scale on Z-axis
 2418        return matrix;
 419    }
 420
 421    /// <inheritdoc cref="SetLossyScale(Fixed64, Fixed64, Fixed64)" />
 422    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1423    public static Fixed3x3 SetLossyScale(Vector3d scale) => SetLossyScale(scale.X, scale.Y, scale.Z);
 424
 425    /// <summary>
 426    /// Creates a scaling matrix (puts the 'scale' vector down the diagonal)
 427    /// </summary>
 428    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 429    public static Fixed3x3 SetLossyScale(Fixed64 x, Fixed64 y, Fixed64 z) =>
 2430        new(x, Fixed64.Zero, Fixed64.Zero,
 2431            Fixed64.Zero, y, Fixed64.Zero,
 2432            Fixed64.Zero, Fixed64.Zero, z);
 433
 434    /// <summary>
 435    /// Applies the provided local scale to the matrix by modifying the diagonal elements.
 436    /// </summary>
 437    /// <param name="matrix">The matrix to set the scale against.</param>
 438    /// <param name="localScale">A Vector3d representing the local scale to apply.</param>
 439    public static Fixed3x3 SetScale(Fixed3x3 matrix, Vector3d localScale)
 440    {
 4441        matrix.M11 = localScale.X; // Apply scale on X-axis
 4442        matrix.M22 = localScale.Y; // Apply scale on Y-axis
 4443        matrix.M33 = localScale.Z; // Apply scale on Z-axis
 444
 4445        return matrix;
 446    }
 447
 448    /// <summary>
 449    /// Sets the global scale of an object using FixedMatrix3x3.
 450    /// Similar to SetGlobalScale for FixedMatrix4x4, but for a 3x3 matrix.
 451    /// </summary>
 452    /// <param name="matrix">The transformation matrix (3x3) representing the object's global state.</param>
 453    /// <param name="globalScale">The desired global scale represented as a Vector3d.</param>
 454    /// <remarks>
 455    /// The method extracts the current global scale from the matrix and computes the new local scale
 456    /// by dividing the desired global scale by the current global scale.
 457    /// The new local scale is then applied to the matrix.
 458    /// </remarks>
 459    public static Fixed3x3 SetGlobalScale(Fixed3x3 matrix, Vector3d globalScale)
 460    {
 461        // normalize the matrix to avoid drift in the rotation component
 2462        matrix.NormalizeInPlace();
 463
 464        // Reset the local scaling portion of the matrix
 2465        matrix.ResetScaleToIdentity();
 466
 467        // Compute the new local scale by dividing the desired global scale by the current global scale
 2468        Vector3d newLocalScale = new(
 2469            globalScale.X / Fixed64.One,
 2470            globalScale.Y / Fixed64.One,
 2471            globalScale.Z / Fixed64.One
 2472        );
 473
 474        // Apply the new local scale to the matrix
 2475        return matrix.SetScale(newLocalScale);
 476    }
 477
 478    /// <summary>
 479    /// Extracts the scaling factors from the matrix by returning the diagonal elements.
 480    /// </summary>
 481    /// <returns>A Vector3d representing the scale along X, Y, and Z axes.</returns>
 482    public static Vector3d ExtractScale(Fixed3x3 matrix) =>
 5483        new(new Vector3d(matrix.M11, matrix.M12, matrix.M13).Magnitude,
 5484            new Vector3d(matrix.M21, matrix.M22, matrix.M23).Magnitude,
 5485            new Vector3d(matrix.M31, matrix.M32, matrix.M33).Magnitude);
 486
 487    /// <summary>
 488    /// Extracts the scaling factors from the matrix by returning the diagonal elements (lossy).
 489    /// </summary>
 490    /// <returns>A Vector3d representing the scale along X, Y, and Z axes (lossy).</returns>
 3491    public static Vector3d ExtractLossyScale(Fixed3x3 matrix) => new(matrix.M11, matrix.M22, matrix.M33);
 492
 493    #endregion
 494
 495    #region Static Matrix Operations
 496
 497    /// <summary>
 498    /// Linearly interpolates between two matrices.
 499    /// </summary>
 500    public static Fixed3x3 Lerp(Fixed3x3 a, Fixed3x3 b, Fixed64 t) =>
 3501        new(FixedMath.Lerp(a.M11, b.M11, t), FixedMath.Lerp(a.M12, b.M12, t), FixedMath.Lerp(a.M13, b.M13, t),
 3502            FixedMath.Lerp(a.M21, b.M21, t), FixedMath.Lerp(a.M22, b.M22, t), FixedMath.Lerp(a.M23, b.M23, t),
 3503            FixedMath.Lerp(a.M31, b.M31, t), FixedMath.Lerp(a.M32, b.M32, t), FixedMath.Lerp(a.M33, b.M33, t));
 504
 505    /// <summary>
 506    /// Transposes the matrix (swaps rows and columns).
 507    /// </summary>
 508    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 509    public static Fixed3x3 Transpose(Fixed3x3 matrix) =>
 3510        new(matrix.M11, matrix.M21, matrix.M31,
 3511            matrix.M12, matrix.M22, matrix.M32,
 3512            matrix.M13, matrix.M23, matrix.M33);
 513
 514    /// <summary>
 515    /// Attempts to invert the matrix. If the determinant is zero, returns false and sets result to null.
 516    /// </summary>
 517    public static bool Invert(Fixed3x3 matrix, out Fixed3x3? result)
 518    {
 519        // Calculate the determinant
 7520        Fixed64 det = matrix.GetDeterminant();
 521
 7522        if (det == Fixed64.Zero)
 523        {
 2524            result = null;
 2525            return false;
 526        }
 527
 528        // Calculate the inverse
 5529        Fixed64 invDet = Fixed64.One / det;
 530
 531        // Compute the inverse matrix
 5532        result = new Fixed3x3(
 5533            invDet * (matrix.M22 * matrix.M33 - matrix.M32 * matrix.M23),
 5534            invDet * (matrix.M13 * matrix.M32 - matrix.M12 * matrix.M33),
 5535            invDet * (matrix.M12 * matrix.M23 - matrix.M13 * matrix.M22),
 5536
 5537            invDet * (matrix.M23 * matrix.M31 - matrix.M21 * matrix.M33),
 5538            invDet * (matrix.M11 * matrix.M33 - matrix.M13 * matrix.M31),
 5539            invDet * (matrix.M13 * matrix.M21 - matrix.M11 * matrix.M23),
 5540
 5541            invDet * (matrix.M21 * matrix.M32 - matrix.M22 * matrix.M31),
 5542            invDet * (matrix.M12 * matrix.M31 - matrix.M11 * matrix.M32),
 5543            invDet * (matrix.M11 * matrix.M22 - matrix.M12 * matrix.M21)
 5544        );
 545
 5546        return true;
 547    }
 548
 549    /// <summary>
 550    /// Transforms a direction vector from local space to world space using this transformation matrix.
 551    /// Ignores translation.
 552    /// </summary>
 553    /// <remarks>
 554    /// FixedMathSharp applies matrices using a row-vector convention: <c>direction * matrix</c>.
 555    /// </remarks>
 556    /// <param name="matrix">The transformation matrix.</param>
 557    /// <param name="direction">The local-space direction vector.</param>
 558    /// <returns>The transformed direction in world space.</returns>
 559    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 560    public static Vector3d TransformDirection(Fixed3x3 matrix, Vector3d direction) =>
 13561        new(direction.X * matrix.M11 + direction.Y * matrix.M21 + direction.Z * matrix.M31,
 13562            direction.X * matrix.M12 + direction.Y * matrix.M22 + direction.Z * matrix.M32,
 13563            direction.X * matrix.M13 + direction.Y * matrix.M23 + direction.Z * matrix.M33);
 564
 565    /// <summary>
 566    /// Transforms a direction from world space into the local space of the matrix.
 567    /// Ignores translation.
 568    /// </summary>
 569    /// <param name="matrix">The transformation matrix.</param>
 570    /// <param name="direction">The world-space direction.</param>
 571    /// <returns>The transformed local-space direction.</returns>
 572    public static Vector3d InverseTransformDirection(Fixed3x3 matrix, Vector3d direction)
 573    {
 5574        bool canInvert = !Invert(matrix, out Fixed3x3? inverseMatrix) || !inverseMatrix.HasValue;
 5575        if (canInvert)
 1576            throw new InvalidOperationException("Matrix is not invertible.");
 577
 4578        return TransformDirection(inverseMatrix!.Value, direction);
 579    }
 580
 581    #endregion
 582
 583    #region Operators
 584
 585    /// <summary>
 586    /// Subtracts each corresponding element of one Fixed3x3 matrix from another.
 587    /// </summary>
 588    /// <param name="a">The first Fixed3x3 matrix (the minuend).</param>
 589    /// <param name="b">The second Fixed3x3 matrix (the subtrahend).</param>
 590    /// <returns>
 591    /// A Fixed3x3 matrix whose elements are the result of subtracting each element of parameter b from the correspondin
 592    /// </returns>
 593    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 594    public static Fixed3x3 operator -(Fixed3x3 a, Fixed3x3 b) =>
 1595        new(a.M11 - b.M11, a.M12 - b.M12, a.M13 - b.M13,
 1596            a.M21 - b.M21, a.M22 - b.M22, a.M23 - b.M23,
 1597            a.M31 - b.M31, a.M32 - b.M32, a.M33 - b.M33);
 598
 599    /// <summary>
 600    /// Adds two Fixed3x3 matrices element-wise.
 601    /// </summary>
 602    /// <param name="a">The first matrix to add.</param>
 603    /// <param name="b">The second matrix to add.</param>
 604    /// <returns>A Fixed3x3 matrix whose elements are the sums of the corresponding elements of the input matrices.</ret
 605    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 606    public static Fixed3x3 operator +(Fixed3x3 a, Fixed3x3 b) =>
 1607        new(a.M11 + b.M11, a.M12 + b.M12, a.M13 + b.M13,
 1608            a.M21 + b.M21, a.M22 + b.M22, a.M23 + b.M23,
 1609            a.M31 + b.M31, a.M32 + b.M32, a.M33 + b.M33);
 610
 611    /// <summary>
 612    /// Negates all elements of the matrix.
 613    /// </summary>
 614    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 615    public static Fixed3x3 operator -(Fixed3x3 a) =>
 1616        new(-a.M11, -a.M12, -a.M13,
 1617            -a.M21, -a.M22, -a.M23,
 1618            -a.M31, -a.M32, -a.M33);
 619
 620    /// <summary>
 621    /// Performs matrix multiplication on two 3x3 matrices.
 622    /// </summary>
 623    /// <remarks>Matrix multiplication is not commutative; the order of operands affects the result.</remarks>
 624    /// <param name="a">The first matrix to multiply.</param>
 625    /// <param name="b">The second matrix to multiply.</param>
 626    /// <returns>A new Fixed3x3 instance that is the product of the two input matrices.</returns>
 627    public static Fixed3x3 operator *(Fixed3x3 a, Fixed3x3 b) =>
 1628        new(a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31,
 1629            a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32,
 1630            a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33,
 1631
 1632            a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31,
 1633            a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32,
 1634            a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33,
 1635
 1636            a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31,
 1637            a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32,
 1638            a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33);
 639
 640    /// <summary>
 641    /// Multiplies each element of the specified matrix by the given scalar value.
 642    /// </summary>
 643    /// <param name="a">The matrix whose elements are to be multiplied.</param>
 644    /// <param name="scalar">The scalar value by which to multiply each element of the matrix.</param>
 645    /// <returns>
 646    /// A new Fixed3x3 matrix whose elements are the result of multiplying each element of the input matrix by the scala
 647    /// </returns>
 648    public static Fixed3x3 operator *(Fixed3x3 a, Fixed64 scalar) =>
 2649        new(a.M11 * scalar, a.M12 * scalar, a.M13 * scalar,
 2650            a.M21 * scalar, a.M22 * scalar, a.M23 * scalar,
 2651            a.M31 * scalar, a.M32 * scalar, a.M33 * scalar);
 652
 653    /// <inheritdoc cref="operator *(Fixed3x3, Fixed64)"/>
 1654    public static Fixed3x3 operator *(Fixed64 scalar, Fixed3x3 a) => a * scalar;
 655
 656    /// <summary>
 657    /// Divides each element of the specified matrix by the given scalar value.
 658    /// </summary>
 659    /// <remarks>
 660    /// Division is performed element-wise.
 661    /// The result may lose precision if the divisor does not evenly divide the matrix elements.
 662    /// </remarks>
 663    /// <param name="a">The matrix whose elements are to be divided.</param>
 664    /// <param name="divisor">The scalar value by which to divide each element of the matrix.</param>
 665    /// <returns>
 666    /// A new Fixed3x3 matrix whose elements are the result of dividing the corresponding elements of the input matrix b
 667    /// </returns>
 668    public static Fixed3x3 operator /(Fixed3x3 a, int divisor) =>
 1669         new(a.M11 / divisor, a.M12 / divisor, a.M13 / divisor,
 1670             a.M21 / divisor, a.M22 / divisor, a.M23 / divisor,
 1671             a.M31 / divisor, a.M32 / divisor, a.M33 / divisor);
 672
 673    /// <summary>
 674    /// Determines whether two Fixed3x3 instances are equal.
 675    /// </summary>
 676    /// <param name="left">The first Fixed3x3 instance to compare.</param>
 677    /// <param name="right">The second Fixed3x3 instance to compare.</param>
 678    /// <returns>true if the specified Fixed3x3 instances are equal; otherwise, false.</returns>
 679    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1680    public static bool operator ==(Fixed3x3 left, Fixed3x3 right) => left.Equals(right);
 681
 682    /// <summary>
 683    /// Determines whether two Fixed3x3 instances are not equal.
 684    /// </summary>
 685    /// <param name="left">The first Fixed3x3 instance to compare.</param>
 686    /// <param name="right">The second Fixed3x3 instance to compare.</param>
 687    /// <returns>true if the specified Fixed3x3 instances are not equal; otherwise, false.</returns>
 688    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1689    public static bool operator !=(Fixed3x3 left, Fixed3x3 right) => !left.Equals(right);
 690
 691    #endregion
 692
 693    #region Equality and HashCode Overrides
 694
 695    /// <inheritdoc/>
 696    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 697    public bool Equals(Fixed3x3 other) =>
 30698        M11 == other.M11 && M12 == other.M12 && M13 == other.M13 &&
 30699        M21 == other.M21 && M22 == other.M22 && M23 == other.M23 &&
 30700        M31 == other.M31 && M32 == other.M32 && M33 == other.M33;
 701
 702
 703    /// <inheritdoc/>
 704    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 5705    public override bool Equals(object? obj) => obj is Fixed3x3 other && Equals(other);
 706
 707    /// <inheritdoc/>
 708    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 709    public override int GetHashCode()
 710    {
 711        unchecked
 712        {
 3713            int hash = 17;
 3714            hash = hash * 23 + M11.GetHashCode();
 3715            hash = hash * 23 + M12.GetHashCode();
 3716            hash = hash * 23 + M13.GetHashCode();
 3717            hash = hash * 23 + M21.GetHashCode();
 3718            hash = hash * 23 + M22.GetHashCode();
 3719            hash = hash * 23 + M23.GetHashCode();
 3720            hash = hash * 23 + M31.GetHashCode();
 3721            hash = hash * 23 + M32.GetHashCode();
 3722            hash = hash * 23 + M33.GetHashCode();
 3723            return hash;
 724        }
 725    }
 726
 727    #endregion
 728
 729    #region Conversion
 730
 731    /// <summary>
 732    /// Returns a string that represents the current matrix in a readable format.
 733    /// </summary>
 734    /// <remarks>
 735    /// This method is useful for debugging or logging the contents of the matrix.
 736    /// The returned string lists the matrix elements in row-major order.
 737    /// </remarks>
 738    /// <returns>A string containing the matrix elements formatted as "[m00, m01, m02; m10, m11, m12; m20, m21, m22]".</
 739    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 54740    public override string ToString() => ToString(null, CultureInfo.InvariantCulture);
 741
 742    /// <summary>
 743    /// Returns a string that represents the current matrix in a readable format.
 744    /// </summary>
 745    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 746    public string ToString(string? format, IFormatProvider? formatProvider)
 747    {
 55748        Fixed3x3 value = this;
 55749        return FixedDiagnosticsFormatter.ToString((Span<char> destination, out int charsWritten) =>
 55750            value.TryFormat(destination, out charsWritten, format.AsSpan(), formatProvider));
 751    }
 752
 753    /// <summary>
 754    /// Formats this matrix into the provided destination buffer.
 755    /// </summary>
 756    public bool TryFormat(
 757        Span<char> destination,
 758        out int charsWritten,
 759        ReadOnlySpan<char> format,
 760        IFormatProvider? provider)
 761    {
 60762        int written = 0;
 60763        if (!FixedDiagnosticsFormatter.Append('[', destination, ref written) ||
 60764            !AppendRow(M11, M12, M13, destination, ref written, format, provider) ||
 60765            !FixedDiagnosticsFormatter.Append("; ", destination, ref written) ||
 60766            !AppendRow(M21, M22, M23, destination, ref written, format, provider) ||
 60767            !FixedDiagnosticsFormatter.Append("; ", destination, ref written) ||
 60768            !AppendRow(M31, M32, M33, destination, ref written, format, provider) ||
 60769            !FixedDiagnosticsFormatter.Append(']', destination, ref written))
 770        {
 1771            charsWritten = 0;
 1772            return false;
 773        }
 774
 59775        charsWritten = written;
 59776        return true;
 777    }
 778
 779    private static bool AppendRow(
 780        Fixed64 x,
 781        Fixed64 y,
 782        Fixed64 z,
 783        Span<char> destination,
 784        ref int charsWritten,
 785        ReadOnlySpan<char> format,
 786        IFormatProvider? provider)
 787    {
 178788        return FixedDiagnosticsFormatter.Append(x, destination, ref charsWritten, format, provider) &&
 178789               FixedDiagnosticsFormatter.Append(", ", destination, ref charsWritten) &&
 178790               FixedDiagnosticsFormatter.Append(y, destination, ref charsWritten, format, provider) &&
 178791               FixedDiagnosticsFormatter.Append(", ", destination, ref charsWritten) &&
 178792               FixedDiagnosticsFormatter.Append(z, destination, ref charsWritten, format, provider);
 793    }
 794
 795    #endregion
 796}

Methods/Properties

.cctor()
.ctor(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
get_Item(System.Int32)
set_Item(System.Int32,FixedMathSharp.Fixed64)
NormalizeInPlace()
ResetScaleToIdentity()
GetDeterminant()
InvertDiagonal()
CreateRotationX(FixedMathSharp.Fixed64)
CreateRotationY(FixedMathSharp.Fixed64)
CreateRotationZ(FixedMathSharp.Fixed64)
CreateShear(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateScale(FixedMathSharp.Vector3d)
CreateScale(FixedMathSharp.Fixed64)
CreateBarycentricProductSums(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetNormalized(FixedMathSharp.Fixed3x3)
ResetScaleToIdentity(FixedMathSharp.Fixed3x3)
SetLossyScale(FixedMathSharp.Vector3d)
SetLossyScale(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
SetScale(FixedMathSharp.Fixed3x3,FixedMathSharp.Vector3d)
SetGlobalScale(FixedMathSharp.Fixed3x3,FixedMathSharp.Vector3d)
ExtractScale(FixedMathSharp.Fixed3x3)
ExtractLossyScale(FixedMathSharp.Fixed3x3)
Lerp(FixedMathSharp.Fixed3x3,FixedMathSharp.Fixed3x3,FixedMathSharp.Fixed64)
Transpose(FixedMathSharp.Fixed3x3)
Invert(FixedMathSharp.Fixed3x3,System.Nullable`1<FixedMathSharp.Fixed3x3>&)
TransformDirection(FixedMathSharp.Fixed3x3,FixedMathSharp.Vector3d)
InverseTransformDirection(FixedMathSharp.Fixed3x3,FixedMathSharp.Vector3d)
op_Subtraction(FixedMathSharp.Fixed3x3,FixedMathSharp.Fixed3x3)
op_Addition(FixedMathSharp.Fixed3x3,FixedMathSharp.Fixed3x3)
op_UnaryNegation(FixedMathSharp.Fixed3x3)
op_Multiply(FixedMathSharp.Fixed3x3,FixedMathSharp.Fixed3x3)
op_Multiply(FixedMathSharp.Fixed3x3,FixedMathSharp.Fixed64)
op_Multiply(FixedMathSharp.Fixed64,FixedMathSharp.Fixed3x3)
op_Division(FixedMathSharp.Fixed3x3,System.Int32)
op_Equality(FixedMathSharp.Fixed3x3,FixedMathSharp.Fixed3x3)
op_Inequality(FixedMathSharp.Fixed3x3,FixedMathSharp.Fixed3x3)
Equals(FixedMathSharp.Fixed3x3)
Equals(System.Object)
GetHashCode()
ToString()
ToString(System.String,System.IFormatProvider)
TryFormat(System.Span`1<System.Char>,System.Int32&,System.ReadOnlySpan`1<System.Char>,System.IFormatProvider)
AppendRow(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Span`1<System.Char>,System.Int32&,System.ReadOnlySpan`1<System.Char>,System.IFormatProvider)