< Summary

Information
Class: FixedMathSharp.Fixed4x4
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Matrices/Fixed4x4.cs
Line coverage
99%
Covered lines: 748
Uncovered lines: 6
Coverable lines: 754
Total lines: 1641
Line coverage: 99.2%
Branch coverage
100%
Covered branches: 148
Total branches: 148
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
FromRows(...)100%11100%
FromColumns(...)100%11100%
get_IsAffine()100%66100%
get_Translation()100%11100%
get_Right()100%11100%
get_Left()100%11100%
get_Up()100%11100%
get_Down()100%11100%
get_Forward()100%11100%
get_Backward()100%11100%
get_Scale()100%11100%
get_Rotation()100%11100%
get_Item(...)100%1717100%
set_Item(...)100%1717100%
GetDeterminant()100%22100%
ResetScaleToIdentity()100%11100%
SetTransform(...)100%11100%
CreateTranslation(...)100%11100%
CreateTranslation(...)100%11100%
CreateRotation(...)100%11100%
CreateRotationX(...)100%11100%
CreateRotationY(...)100%11100%
CreateRotationZ(...)100%11100%
CreateFromAxisAngle(...)100%11100%
CreateFromEulerAngles(...)100%11100%
CreateScale(...)100%11100%
CreateScale(...)100%210%
CreateScale(...)100%210%
CreateLookAt(...)100%44100%
CreateOrthographic(...)100%44100%
CreateOrthographicOffCenter(...)100%44100%
CreatePerspective(...)100%44100%
CreatePerspectiveFieldOfView(...)100%66100%
CreatePerspectiveOffCenter(...)100%44100%
CreateWorld(...)100%44100%
CreateTransform(...)100%11100%
ScaleRotateTranslate(...)100%11100%
TranslateRotateScale(...)100%11100%
ExtractTranslation(...)100%11100%
ExtractRight(...)100%11100%
ExtractUp(...)100%11100%
ExtractForward(...)100%11100%
ExtractScale(...)100%11100%
ExtractLossyScale(...)100%11100%
ExtractRotation(...)100%66100%
Decompose(...)100%88100%
SetTranslation(...)100%11100%
SetScale(...)100%11100%
ApplyScaleToRotation(...)100%11100%
ResetScaleToIdentity(...)100%11100%
SetGlobalScale(...)100%11100%
SetRotation(...)100%11100%
NormalizeRotationMatrix(...)100%11100%
Lerp(...)100%11100%
Transpose(...)100%11100%
ComponentDivide(...)100%11100%
Divide(...)100%22100%
Invert(...)100%44100%
FullInvert(...)100%22100%
Transform(...)100%11100%
TransformPoint(...)100%22100%
FullTransformPoint(...)100%22100%
InverseTransformPoint(...)100%44100%
FullInverseTransformPoint(...)100%22100%
op_UnaryNegation(...)100%11100%
op_Addition(...)100%11100%
op_Subtraction(...)100%11100%
op_Multiply(...)100%44100%
op_Multiply(...)100%11100%
op_Multiply(...)100%11100%
op_Division(...)100%11100%
op_Equality(...)100%11100%
op_Inequality(...)100%11100%
Equals(...)100%22100%
Equals(...)100%3030100%
GetHashCode()100%11100%
FromRotationMatrix(...)100%11100%
ValidateDepthRange(...)100%44100%
ValidatePerspectiveDepthRange(...)100%44100%

File(s)

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

#LineLine coverage
 1using MemoryPack;
 2using System;
 3using System.Runtime.CompilerServices;
 4using System.Text.Json.Serialization;
 5
 6namespace FixedMathSharp;
 7
 8/// <summary>
 9/// Represents a 4x4 matrix used for transformations in 3D space, including translation, rotation, scaling, and perspect
 10/// </summary>
 11/// <remarks>
 12/// A 4x4 matrix is the standard structure for 3D transformations because it can handle both linear transformations (rot
 13/// and affine transformations (translation, shearing, and perspective projections).
 14/// It is commonly used in graphics pipelines, game engines, and 3D rendering systems.
 15///
 16/// Use Cases:
 17/// - Transforming objects in 3D space (position, orientation, and size).
 18/// - Combining multiple transformations (e.g., model-view-projection matrices).
 19/// - Applying translations, which require an extra dimension for homogeneous coordinates.
 20/// - Useful in animation, physics engines, and 3D rendering for full transformation control.
 21/// </remarks>
 22[Serializable]
 23[MemoryPackable]
 24public partial struct Fixed4x4 : IEquatable<Fixed4x4>
 25{
 26    #region Static Readonly Fields
 27
 28    /// <summary>
 29    /// Returns the identity matrix (diagonal elements set to 1).
 30    /// </summary>
 31    public static readonly Fixed4x4 Identity = new(
 32        Fixed64.One, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 33        Fixed64.Zero, Fixed64.One, Fixed64.Zero, Fixed64.Zero,
 34        Fixed64.Zero, Fixed64.Zero, Fixed64.One, Fixed64.Zero,
 35        Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One);
 36
 37    /// <summary>
 38    /// Returns a matrix with all elements set to zero.
 39    /// </summary>
 40    public static readonly Fixed4x4 Zero = new(
 41        Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 42        Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 43        Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 44        Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero);
 45
 46    #endregion
 47
 48    #region Fields and Constants
 49
 50    // First row
 51
 52    /// <summary>
 53    /// Represents the element in the first row and first column of the matrix.
 54    /// </summary>
 55    [JsonInclude]
 56    [MemoryPackOrder(0)]
 57    public Fixed64 m00;
 58    /// <summary>
 59    /// Represents the element in the first row and second column of the matrix.
 60    /// </summary>
 61    [JsonInclude]
 62    [MemoryPackOrder(1)]
 63    public Fixed64 m01;
 64    /// <summary>
 65    /// Represents the element in the first row and third column of the matrix.
 66    /// </summary>
 67    [JsonInclude]
 68    [MemoryPackOrder(2)]
 69    public Fixed64 m02;
 70    /// <summary>
 71    /// Represents the element in the first row and fourth column of the matrix.
 72    /// </summary>
 73    [JsonInclude]
 74    [MemoryPackOrder(3)]
 75    public Fixed64 m03;
 76
 77    // Second row
 78
 79    /// <summary>
 80    /// Represents the element in the second row and first column of the matrix.
 81    /// </summary>
 82    [JsonInclude]
 83    [MemoryPackOrder(4)]
 84    public Fixed64 m10;
 85    /// <summary>
 86    /// Represents the element in the second row and second column of the matrix.
 87    /// </summary>
 88    [JsonInclude]
 89    [MemoryPackOrder(5)]
 90    public Fixed64 m11;
 91    /// <summary>
 92    /// Represents the element in the second row and third column of the matrix.
 93    /// </summary>
 94    [JsonInclude]
 95    [MemoryPackOrder(6)]
 96    public Fixed64 m12;
 97    /// <summary>
 98    /// Represents the element in the second row and fourth column of the matrix.
 99    /// </summary>
 100    [JsonInclude]
 101    [MemoryPackOrder(7)]
 102    public Fixed64 m13;
 103
 104    // Third row
 105
 106    /// <summary>
 107    /// Represents the element in the third row and first column of the matrix.
 108    /// </summary>
 109    [JsonInclude]
 110    [MemoryPackOrder(8)]
 111    public Fixed64 m20;
 112    /// <summary>
 113    /// Represents the element in the third row and second column of the matrix.
 114    /// </summary>
 115    [JsonInclude]
 116    [MemoryPackOrder(9)]
 117    public Fixed64 m21;
 118    /// <summary>
 119    /// Represents the element in the third row and third column of the matrix.
 120    /// </summary>
 121    [JsonInclude]
 122    [MemoryPackOrder(10)]
 123    public Fixed64 m22;
 124    /// <summary>
 125    /// Represents the element in the third row and fourth column of the matrix.
 126    /// </summary>
 127    [JsonInclude]
 128    [MemoryPackOrder(11)]
 129    public Fixed64 m23;
 130
 131    // Fourth row
 132
 133    /// <summary>
 134    /// Represents the element in the fourth row and first column of the matrix.
 135    /// </summary>
 136    [JsonInclude]
 137    [MemoryPackOrder(12)]
 138    public Fixed64 m30;
 139    /// <summary>
 140    /// Represents the element in the fourth row and second column of the matrix.
 141    /// </summary>
 142    [JsonInclude]
 143    [MemoryPackOrder(13)]
 144    public Fixed64 m31;
 145    /// <summary>
 146    /// Represents the element in the fourth row and third column of the matrix.
 147    /// </summary>
 148    [JsonInclude]
 149    [MemoryPackOrder(14)]
 150    public Fixed64 m32;
 151    /// <summary>
 152    /// Represents the element in the fourth row and fourth column of the matrix.
 153    /// </summary>
 154    [JsonInclude]
 155    [MemoryPackOrder(15)]
 156    public Fixed64 m33;
 157
 158    #endregion
 159
 160    #region Constructors
 161
 162    /// <summary>
 163    /// Initializes a new FixedMatrix4x4 with individual elements.
 164    /// </summary>
 165    public Fixed4x4(
 166        Fixed64 m00, Fixed64 m01, Fixed64 m02, Fixed64 m03,
 167        Fixed64 m10, Fixed64 m11, Fixed64 m12, Fixed64 m13,
 168        Fixed64 m20, Fixed64 m21, Fixed64 m22, Fixed64 m23,
 169        Fixed64 m30, Fixed64 m31, Fixed64 m32, Fixed64 m33
 170    )
 217171    {
 868172        this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03;
 868173        this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m13 = m13;
 868174        this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23;
 868175        this.m30 = m30; this.m31 = m31; this.m32 = m32; this.m33 = m33;
 217176    }
 177
 178    /// <summary>
 179    /// Creates a matrix from four row vectors.
 180    /// </summary>
 181    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 182    public static Fixed4x4 FromRows(Vector4d row0, Vector4d row1, Vector4d row2, Vector4d row3)
 1183    {
 1184        return new Fixed4x4(
 1185            row0.x, row0.y, row0.z, row0.w,
 1186            row1.x, row1.y, row1.z, row1.w,
 1187            row2.x, row2.y, row2.z, row2.w,
 1188            row3.x, row3.y, row3.z, row3.w);
 1189    }
 190
 191    /// <summary>
 192    /// Creates a matrix from four column vectors.
 193    /// </summary>
 194    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 195    public static Fixed4x4 FromColumns(Vector4d column0, Vector4d column1, Vector4d column2, Vector4d column3)
 1196    {
 1197        return new Fixed4x4(
 1198            column0.x, column1.x, column2.x, column3.x,
 1199            column0.y, column1.y, column2.y, column3.y,
 1200            column0.z, column1.z, column2.z, column3.z,
 1201            column0.w, column1.w, column2.w, column3.w);
 1202    }
 203
 204    #endregion
 205
 206    #region Properties
 207
 208    /// <summary>
 209    /// Gets a value indicating whether the matrix represents an affine transformation.
 210    /// </summary>
 211    /// <remarks>
 212    /// An affine transformation is one where the bottom row is (0, 0, 0, 1),
 213    /// allowing for efficient operations such as translation, scaling, rotation, and shearing without perspective disto
 214    /// </remarks>
 215    [JsonIgnore]
 216    [MemoryPackIgnore]
 149217    public readonly bool IsAffine => m33 == Fixed64.One
 149218        && m03 == Fixed64.Zero
 149219        && m13 == Fixed64.Zero
 149220        && m23 == Fixed64.Zero;
 221
 222    /// <inheritdoc cref="ExtractTranslation(Fixed4x4)" />
 223    [JsonIgnore]
 224    [MemoryPackIgnore]
 53225    public readonly Vector3d Translation => ExtractTranslation(this);
 226
 227    /// <summary>
 228    /// Gets the right direction vector for this instance.
 229    /// </summary>
 230    [JsonIgnore]
 231    [MemoryPackIgnore]
 46232    public readonly Vector3d Right => ExtractRight(this);
 233
 234    /// <summary>
 235    /// Gets the left direction vector for this instance.
 236    /// </summary>
 237    [JsonIgnore]
 238    [MemoryPackIgnore]
 45239    public readonly Vector3d Left => -ExtractRight(this);
 240
 241    /// <summary>
 242    /// Gets the upward direction vector for this instance.
 243    /// </summary>
 244    [JsonIgnore]
 245    [MemoryPackIgnore]
 46246    public readonly Vector3d Up => ExtractUp(this);
 247
 248    /// <summary>
 249    /// Gets the downward direction vector for this instance.
 250    /// </summary>
 251    [JsonIgnore]
 252    [MemoryPackIgnore]
 45253    public readonly Vector3d Down => -ExtractUp(this);
 254
 255    /// <summary>
 256    /// Gets the forward direction vector for this instance.
 257    /// </summary>
 258    [JsonIgnore]
 259    [MemoryPackIgnore]
 46260    public readonly Vector3d Forward => ExtractForward(this);
 261
 262    /// <summary>
 263    /// Gets the backward direction vector for this instance.
 264    /// </summary>
 265    [JsonIgnore]
 266    [MemoryPackIgnore]
 45267    public readonly Vector3d Backward => -ExtractForward(this);
 268
 269    /// <inheritdoc cref="ExtractScale(Fixed4x4)" />
 270    [JsonIgnore]
 271    [MemoryPackIgnore]
 51272    public readonly Vector3d Scale => ExtractScale(this);
 273
 274    /// <inheritdoc cref="ExtractRotation(Fixed4x4)" />
 275    [JsonIgnore]
 276    [MemoryPackIgnore]
 50277    public readonly FixedQuaternion Rotation => ExtractRotation(this);
 278
 279    /// <summary>
 280    /// Gets or sets the matrix element at the specified linear index.
 281    /// </summary>
 282    /// <remarks>Matrix elements are indexed in row-major order from 0 to 15.</remarks>
 283    /// <param name="index">The zero-based linear index of the matrix element to get or set. Must be in the range 0 to 1
 284    /// <returns>The matrix element at the specified index.</returns>
 285    /// <exception cref="IndexOutOfRangeException">Thrown when the specified index is less than 0 or greater than 15.</e
 286    [JsonIgnore]
 287    [MemoryPackIgnore]
 288    public Fixed64 this[int index]
 289    {
 290        get
 18291        {
 18292            return index switch
 18293            {
 1294                0 => m00,
 1295                1 => m10,
 1296                2 => m20,
 1297                3 => m30,
 1298                4 => m01,
 1299                5 => m11,
 1300                6 => m21,
 1301                7 => m31,
 1302                8 => m02,
 1303                9 => m12,
 1304                10 => m22,
 1305                11 => m32,
 1306                12 => m03,
 1307                13 => m13,
 1308                14 => m23,
 1309                15 => m33,
 2310                _ => throw new IndexOutOfRangeException("Invalid matrix index!"),
 18311            };
 16312        }
 313        set
 18314        {
 18315            switch (index)
 316            {
 317                case 0:
 1318                    m00 = value;
 1319                    break;
 320                case 1:
 1321                    m10 = value;
 1322                    break;
 323                case 2:
 1324                    m20 = value;
 1325                    break;
 326                case 3:
 1327                    m30 = value;
 1328                    break;
 329                case 4:
 1330                    m01 = value;
 1331                    break;
 332                case 5:
 1333                    m11 = value;
 1334                    break;
 335                case 6:
 1336                    m21 = value;
 1337                    break;
 338                case 7:
 1339                    m31 = value;
 1340                    break;
 341                case 8:
 1342                    m02 = value;
 1343                    break;
 344                case 9:
 1345                    m12 = value;
 1346                    break;
 347                case 10:
 1348                    m22 = value;
 1349                    break;
 350                case 11:
 1351                    m32 = value;
 1352                    break;
 353                case 12:
 1354                    m03 = value;
 1355                    break;
 356                case 13:
 1357                    m13 = value;
 1358                    break;
 359                case 14:
 1360                    m23 = value;
 1361                    break;
 362                case 15:
 1363                    m33 = value;
 1364                    break;
 365                default:
 2366                    throw new IndexOutOfRangeException("Invalid matrix index!");
 367            }
 16368        }
 369    }
 370
 371    #endregion
 372
 373    #region Methods (Instance)
 374
 375    /// <summary>
 376    /// Calculates the determinant of a 4x4 matrix.
 377    /// </summary>
 378    public Fixed64 GetDeterminant()
 20379    {
 20380        if (IsAffine)
 12381        {
 12382            return m00 * (m11 * m22 - m12 * m21)
 12383                 - m01 * (m10 * m22 - m12 * m20)
 12384                 + m02 * (m10 * m21 - m11 * m20);
 385        }
 386
 387        // Process as full 4x4 matrix
 8388        Fixed64 minor0 = m22 * m33 - m23 * m32;
 8389        Fixed64 minor1 = m21 * m33 - m23 * m31;
 8390        Fixed64 minor2 = m21 * m32 - m22 * m31;
 8391        Fixed64 cofactor0 = m20 * m33 - m23 * m30;
 8392        Fixed64 cofactor1 = m20 * m32 - m22 * m30;
 8393        Fixed64 cofactor2 = m20 * m31 - m21 * m30;
 8394        return m00 * (m11 * minor0 - m12 * minor1 + m13 * minor2)
 8395            - m01 * (m10 * minor0 - m12 * cofactor0 + m13 * cofactor1)
 8396            + m02 * (m10 * minor1 - m11 * cofactor0 + m13 * cofactor2)
 8397            - m03 * (m10 * minor2 - m11 * cofactor1 + m12 * cofactor2);
 20398    }
 399
 400    /// <inheritdoc cref="Fixed4x4.ResetScaleToIdentity(Fixed4x4)" />
 401    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 402    public Fixed4x4 ResetScaleToIdentity()
 2403    {
 2404        return this = ResetScaleToIdentity(this);
 2405    }
 406
 407    /// <summary>
 408    /// Sets the translation, scale, and rotation components onto the matrix.
 409    /// </summary>
 410    /// <param name="translation">The translation vector.</param>
 411    /// <param name="scale">The scale vector.</param>
 412    /// <param name="rotation">The rotation quaternion.</param>
 413    public void SetTransform(Vector3d translation, FixedQuaternion rotation, Vector3d scale)
 5414    {
 5415        this = CreateTransform(translation, rotation, scale);
 5416    }
 417
 418    #endregion
 419
 420    #region Static Matrix Generators and Transformations
 421
 422    /// <summary>
 423    /// Creates a translation matrix from the specified 3-dimensional vector.
 424    /// </summary>
 425    /// <param name="position"></param>
 426    /// <returns>The translation matrix.</returns>
 427    public static Fixed4x4 CreateTranslation(Vector3d position)
 14428    {
 14429        Fixed4x4 result = default;
 14430        result.m00 = Fixed64.One;
 14431        result.m01 = Fixed64.Zero;
 14432        result.m02 = Fixed64.Zero;
 14433        result.m03 = Fixed64.Zero;
 14434        result.m10 = Fixed64.Zero;
 14435        result.m11 = Fixed64.One;
 14436        result.m12 = Fixed64.Zero;
 14437        result.m13 = Fixed64.Zero;
 14438        result.m20 = Fixed64.Zero;
 14439        result.m21 = Fixed64.Zero;
 14440        result.m22 = Fixed64.One;
 14441        result.m23 = Fixed64.Zero;
 14442        result.m30 = position.x;
 14443        result.m31 = position.y;
 14444        result.m32 = position.z;
 14445        result.m33 = Fixed64.One;
 14446        return result;
 14447    }
 448
 449    /// <summary>
 450    /// Creates a translation matrix from the specified coordinates.
 451    /// </summary>
 452    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 453    public static Fixed4x4 CreateTranslation(Fixed64 x, Fixed64 y, Fixed64 z)
 1454    {
 1455        return CreateTranslation(new Vector3d(x, y, z));
 1456    }
 457
 458    /// <summary>
 459    /// Creates a rotation matrix from a quaternion.
 460    /// </summary>
 461    /// <param name="rotation">The quaternion representing the rotation.</param>
 462    /// <returns>A 4x4 matrix representing the rotation.</returns>
 463    public static Fixed4x4 CreateRotation(FixedQuaternion rotation)
 26464    {
 26465        Fixed3x3 rotationMatrix = rotation.ToMatrix3x3();
 466
 26467        return new Fixed4x4(
 26468            rotationMatrix.m00, rotationMatrix.m01, rotationMatrix.m02, Fixed64.Zero,
 26469            rotationMatrix.m10, rotationMatrix.m11, rotationMatrix.m12, Fixed64.Zero,
 26470            rotationMatrix.m20, rotationMatrix.m21, rotationMatrix.m22, Fixed64.Zero,
 26471            Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One
 26472        );
 26473    }
 474
 475    /// <summary>
 476    /// Creates a rotation matrix around the X axis.
 477    /// </summary>
 478    public static Fixed4x4 CreateRotationX(Fixed64 angle)
 1479    {
 1480        return FromRotationMatrix(Fixed3x3.CreateRotationX(angle));
 1481    }
 482
 483    /// <summary>
 484    /// Creates a rotation matrix around the Y axis.
 485    /// </summary>
 486    public static Fixed4x4 CreateRotationY(Fixed64 angle)
 1487    {
 1488        return FromRotationMatrix(Fixed3x3.CreateRotationY(angle));
 1489    }
 490
 491    /// <summary>
 492    /// Creates a rotation matrix around the Z axis.
 493    /// </summary>
 494    public static Fixed4x4 CreateRotationZ(Fixed64 angle)
 1495    {
 1496        return FromRotationMatrix(Fixed3x3.CreateRotationZ(angle));
 1497    }
 498
 499    /// <summary>
 500    /// Creates a rotation matrix from an axis and angle.
 501    /// </summary>
 502    public static Fixed4x4 CreateFromAxisAngle(Vector3d axis, Fixed64 angle)
 1503    {
 1504        return CreateRotation(FixedQuaternion.FromAxisAngle(axis, angle));
 1505    }
 506
 507    /// <summary>
 508    /// Creates a rotation matrix from pitch, yaw, and roll angles in radians.
 509    /// </summary>
 510    public static Fixed4x4 CreateFromEulerAngles(Fixed64 pitch, Fixed64 yaw, Fixed64 roll)
 1511    {
 1512        return CreateRotation(FixedQuaternion.FromEulerAngles(pitch, yaw, roll));
 1513    }
 514
 515    /// <summary>
 516    /// Creates a scale matrix from a 3-dimensional vector.
 517    /// </summary>
 518    /// <param name="scale">The vector representing the scale along each axis.</param>
 519    /// <returns>A 4x4 matrix representing the scale transformation.</returns>
 520    public static Fixed4x4 CreateScale(Vector3d scale)
 22521    {
 22522        return new Fixed4x4(
 22523            scale.x, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 22524            Fixed64.Zero, scale.y, Fixed64.Zero, Fixed64.Zero,
 22525            Fixed64.Zero, Fixed64.Zero, scale.z, Fixed64.Zero,
 22526            Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One
 22527        );
 22528    }
 529
 530    /// <summary>
 531    /// Creates a uniform scale matrix.
 532    /// </summary>
 533    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 534    public static Fixed4x4 CreateScale(Fixed64 scale)
 0535    {
 0536        return CreateScale(new Vector3d(scale, scale, scale));
 0537    }
 538
 539    /// <summary>
 540    /// Creates a non-uniform scale matrix from individual scale components.
 541    /// </summary>
 542    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 543    public static Fixed4x4 CreateScale(Fixed64 x, Fixed64 y, Fixed64 z)
 0544    {
 0545        return CreateScale(new Vector3d(x, y, z));
 0546    }
 547
 548    /// <summary>
 549    /// Creates a view matrix looking from a camera position toward a target.
 550    /// </summary>
 551    public static Fixed4x4 CreateLookAt(Vector3d cameraPosition, Vector3d cameraTarget, Vector3d cameraUpVector)
 4552    {
 4553        Vector3d forward = cameraTarget - cameraPosition;
 4554        if (forward.SqrMagnitude == Fixed64.Zero)
 1555            throw new ArgumentException("Camera position and target must be different.", nameof(cameraTarget));
 556
 3557        forward = forward.Normalize();
 3558        Vector3d right = Vector3d.Cross(cameraUpVector, forward);
 3559        if (right.SqrMagnitude == Fixed64.Zero)
 1560            throw new ArgumentException("Camera up vector must not be parallel to the view direction.", nameof(cameraUpV
 561
 2562        right = right.Normalize();
 2563        Vector3d up = Vector3d.Cross(forward, right).Normalize();
 564
 2565        return new Fixed4x4(
 2566            right.x, right.y, right.z, Fixed64.Zero,
 2567            up.x, up.y, up.z, Fixed64.Zero,
 2568            forward.x, forward.y, forward.z, Fixed64.Zero,
 2569            -Vector3d.Dot(right, cameraPosition),
 2570            -Vector3d.Dot(up, cameraPosition),
 2571            -Vector3d.Dot(forward, cameraPosition),
 2572            Fixed64.One);
 2573    }
 574
 575    /// <summary>
 576    /// Creates an orthographic projection matrix centered on the origin.
 577    /// </summary>
 578    public static Fixed4x4 CreateOrthographic(Fixed64 width, Fixed64 height, Fixed64 zNearPlane, Fixed64 zFarPlane)
 5579    {
 5580        if (width <= Fixed64.Zero)
 1581            throw new ArgumentOutOfRangeException(nameof(width), width, "Width must be greater than zero.");
 4582        if (height <= Fixed64.Zero)
 1583            throw new ArgumentOutOfRangeException(nameof(height), height, "Height must be greater than zero.");
 584
 3585        Fixed64 halfWidth = width * Fixed64.Half;
 3586        Fixed64 halfHeight = height * Fixed64.Half;
 3587        return CreateOrthographicOffCenter(-halfWidth, halfWidth, -halfHeight, halfHeight, zNearPlane, zFarPlane);
 1588    }
 589
 590    /// <summary>
 591    /// Creates an off-center orthographic projection matrix.
 592    /// </summary>
 593    public static Fixed4x4 CreateOrthographicOffCenter(
 594        Fixed64 left,
 595        Fixed64 right,
 596        Fixed64 bottom,
 597        Fixed64 top,
 598        Fixed64 zNearPlane,
 599        Fixed64 zFarPlane)
 8600    {
 8601        if (left == right)
 1602            throw new ArgumentOutOfRangeException(nameof(right), right, "Right must be different from left.");
 7603        if (bottom == top)
 1604            throw new ArgumentOutOfRangeException(nameof(top), top, "Top must be different from bottom.");
 6605        ValidateDepthRange(zNearPlane, zFarPlane);
 606
 2607        Fixed64 width = right - left;
 2608        Fixed64 height = top - bottom;
 2609        Fixed64 depth = zFarPlane - zNearPlane;
 610
 2611        return new Fixed4x4(
 2612            Fixed64.Two / width, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 2613            Fixed64.Zero, Fixed64.Two / height, Fixed64.Zero, Fixed64.Zero,
 2614            Fixed64.Zero, Fixed64.Zero, Fixed64.One / depth, Fixed64.Zero,
 2615            (left + right) / (left - right),
 2616            (top + bottom) / (bottom - top),
 2617            -zNearPlane / depth,
 2618            Fixed64.One);
 2619    }
 620
 621    /// <summary>
 622    /// Creates a perspective projection matrix centered on the near plane.
 623    /// </summary>
 624    public static Fixed4x4 CreatePerspective(
 625        Fixed64 width,
 626        Fixed64 height,
 627        Fixed64 nearPlaneDistance,
 628        Fixed64 farPlaneDistance)
 5629    {
 5630        if (width <= Fixed64.Zero)
 1631            throw new ArgumentOutOfRangeException(nameof(width), width, "Width must be greater than zero.");
 4632        if (height <= Fixed64.Zero)
 1633            throw new ArgumentOutOfRangeException(nameof(height), height, "Height must be greater than zero.");
 3634        ValidatePerspectiveDepthRange(nearPlaneDistance, farPlaneDistance);
 635
 1636        Fixed64 depth = farPlaneDistance - nearPlaneDistance;
 1637        Fixed64 twoNear = Fixed64.Two * nearPlaneDistance;
 638
 1639        return new Fixed4x4(
 1640            twoNear / width, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 1641            Fixed64.Zero, twoNear / height, Fixed64.Zero, Fixed64.Zero,
 1642            Fixed64.Zero, Fixed64.Zero, farPlaneDistance / depth, Fixed64.One,
 1643            Fixed64.Zero, Fixed64.Zero, -(nearPlaneDistance * farPlaneDistance) / depth, Fixed64.Zero);
 1644    }
 645
 646    /// <summary>
 647    /// Creates a perspective projection matrix from a vertical field of view.
 648    /// </summary>
 649    public static Fixed4x4 CreatePerspectiveFieldOfView(
 650        Fixed64 fieldOfView,
 651        Fixed64 aspectRatio,
 652        Fixed64 nearPlaneDistance,
 653        Fixed64 farPlaneDistance)
 7654    {
 7655        if (fieldOfView <= Fixed64.Zero || fieldOfView >= FixedMath.PI)
 2656            throw new ArgumentOutOfRangeException(nameof(fieldOfView), fieldOfView, "Field of view must be greater than 
 5657        if (aspectRatio <= Fixed64.Zero)
 1658            throw new ArgumentOutOfRangeException(nameof(aspectRatio), aspectRatio, "Aspect ratio must be greater than z
 4659        ValidatePerspectiveDepthRange(nearPlaneDistance, farPlaneDistance);
 660
 2661        Fixed64 yScale = Fixed64.One / FixedMath.Tan(fieldOfView * Fixed64.Half);
 2662        Fixed64 xScale = yScale / aspectRatio;
 2663        Fixed64 depth = farPlaneDistance - nearPlaneDistance;
 664
 2665        return new Fixed4x4(
 2666            xScale, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 2667            Fixed64.Zero, yScale, Fixed64.Zero, Fixed64.Zero,
 2668            Fixed64.Zero, Fixed64.Zero, farPlaneDistance / depth, Fixed64.One,
 2669            Fixed64.Zero, Fixed64.Zero, -(nearPlaneDistance * farPlaneDistance) / depth, Fixed64.Zero);
 2670    }
 671
 672    /// <summary>
 673    /// Creates an off-center perspective projection matrix.
 674    /// </summary>
 675    public static Fixed4x4 CreatePerspectiveOffCenter(
 676        Fixed64 left,
 677        Fixed64 right,
 678        Fixed64 bottom,
 679        Fixed64 top,
 680        Fixed64 nearPlaneDistance,
 681        Fixed64 farPlaneDistance)
 5682    {
 5683        if (left == right)
 1684            throw new ArgumentOutOfRangeException(nameof(right), right, "Right must be different from left.");
 4685        if (bottom == top)
 1686            throw new ArgumentOutOfRangeException(nameof(top), top, "Top must be different from bottom.");
 3687        ValidatePerspectiveDepthRange(nearPlaneDistance, farPlaneDistance);
 688
 1689        Fixed64 width = right - left;
 1690        Fixed64 height = top - bottom;
 1691        Fixed64 depth = farPlaneDistance - nearPlaneDistance;
 1692        Fixed64 twoNear = Fixed64.Two * nearPlaneDistance;
 693
 1694        return new Fixed4x4(
 1695            twoNear / width, Fixed64.Zero, Fixed64.Zero, Fixed64.Zero,
 1696            Fixed64.Zero, twoNear / height, Fixed64.Zero, Fixed64.Zero,
 1697            (left + right) / (left - right),
 1698            (top + bottom) / (bottom - top),
 1699            farPlaneDistance / depth,
 1700            Fixed64.One,
 1701            Fixed64.Zero, Fixed64.Zero, -(nearPlaneDistance * farPlaneDistance) / depth, Fixed64.Zero);
 1702    }
 703
 704    /// <summary>
 705    /// Creates a world matrix from a position and orientation basis.
 706    /// </summary>
 707    public static Fixed4x4 CreateWorld(Vector3d position, Vector3d forward, Vector3d up)
 3708    {
 3709        if (forward.SqrMagnitude == Fixed64.Zero)
 1710            throw new ArgumentException("Forward vector must be non-zero.", nameof(forward));
 711
 2712        forward = forward.Normalize();
 2713        Vector3d right = Vector3d.Cross(up, forward);
 2714        if (right.SqrMagnitude == Fixed64.Zero)
 1715            throw new ArgumentException("Up vector must not be parallel to forward.", nameof(up));
 716
 1717        right = right.Normalize();
 1718        up = Vector3d.Cross(forward, right).Normalize();
 719
 1720        return new Fixed4x4(
 1721            right.x, right.y, right.z, Fixed64.Zero,
 1722            up.x, up.y, up.z, Fixed64.Zero,
 1723            forward.x, forward.y, forward.z, Fixed64.Zero,
 1724            position.x, position.y, position.z, Fixed64.One);
 1725    }
 726
 727    /// <summary>
 728    /// Constructs a transformation matrix from translation, scale, and rotation.
 729    /// This method ensures that the rotation is properly normalized, applies the scale to the
 730    /// rotational basis, and sets the translation component separately.
 731    /// </summary>
 732    /// <remarks>
 733    /// - Uses a normalized rotation matrix to maintain numerical stability.
 734    /// - Applies non-uniform scaling to the rotation before setting translation.
 735    /// - Preferred when ensuring transformations remain mathematically correct.
 736    /// - If the rotation is already normalized and combined transformations are needed, consider using <see cref="Scale
 737    /// </remarks>
 738    /// <param name="translation">The translation vector.</param>
 739    /// <param name="scale">The scale vector.</param>
 740    /// <param name="rotation">The rotation quaternion.</param>
 741    /// <returns>A transformation matrix incorporating translation, rotation, and scale.</returns>
 742    public static Fixed4x4 CreateTransform(Vector3d translation, FixedQuaternion rotation, Vector3d scale)
 10743    {
 744        // Create the rotation matrix and normalize it
 10745        Fixed4x4 rotationMatrix = CreateRotation(rotation);
 10746        rotationMatrix = NormalizeRotationMatrix(rotationMatrix);
 747
 748        // Apply scale directly to the rotation matrix
 10749        rotationMatrix = ApplyScaleToRotation(rotationMatrix, scale);
 750
 751        // Apply the translation to the combined matrix
 10752        rotationMatrix = SetTranslation(rotationMatrix, translation);
 753
 10754        return rotationMatrix;
 10755    }
 756
 757    /// <summary>
 758    /// Constructs a transformation matrix from translation, rotation, and scale by multiplying
 759    /// separate matrices in the order: Scale * Rotation * Translation.
 760    /// </summary>
 761    /// <remarks>
 762    /// - This method directly multiplies the scale, rotation, and translation matrices.
 763    /// - Ensures that scale is applied first to preserve correct axis scaling.
 764    /// - Then rotation is applied so that rotation is not affected by non-uniform scaling.
 765    /// - Finally, translation moves the object to its correct world position.
 766    /// </remarks>
 767    public static Fixed4x4 ScaleRotateTranslate(Vector3d translation, FixedQuaternion rotation, Vector3d scale)
 8768    {
 769        // Create translation matrix
 8770        Fixed4x4 translationMatrix = CreateTranslation(translation);
 771
 772        // Create rotation matrix using the quaternion
 8773        Fixed4x4 rotationMatrix = CreateRotation(rotation);
 774
 775        // Create scaling matrix
 8776        Fixed4x4 scalingMatrix = CreateScale(scale);
 777
 778        // Combine all transformations
 8779        return (scalingMatrix * rotationMatrix) * translationMatrix;
 8780    }
 781
 782    /// <summary>
 783    /// Constructs a transformation matrix from translation, rotation, and scale by multiplying
 784    /// matrices in the order: Translation * Rotation * Scale (T * R * S).
 785    /// </summary>
 786    /// <remarks>
 787    /// - Use this method when transformations need to be applied **relative to an object's local origin**.
 788    /// - Example use cases include **animation systems**, **hierarchical transformations**, and **UI transformations**.
 789    /// - If you need to apply world-space transformations, use <see cref="CreateTransform"/> instead.
 790    /// </remarks>
 791    public static Fixed4x4 TranslateRotateScale(Vector3d translation, FixedQuaternion rotation, Vector3d scale)
 1792    {
 793        // Create translation matrix
 1794        Fixed4x4 translationMatrix = CreateTranslation(translation);
 795
 796        // Create rotation matrix using the quaternion
 1797        Fixed4x4 rotationMatrix = CreateRotation(rotation);
 798
 799        // Create scaling matrix
 1800        Fixed4x4 scalingMatrix = CreateScale(scale);
 801
 802        // Combine all transformations
 1803        return (translationMatrix * rotationMatrix) * scalingMatrix;
 1804    }
 805
 806    #endregion
 807
 808    #region Decomposition, Extraction, and Setters
 809
 810    /// <summary>
 811    /// Extracts the translation component from the 4x4 matrix.
 812    /// </summary>
 813    /// <param name="matrix">The matrix from which to extract the translation.</param>
 814    /// <returns>A Vector3d representing the translation component.</returns>
 815    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 816    public static Vector3d ExtractTranslation(Fixed4x4 matrix)
 53817    {
 53818        return new Vector3d(matrix.m30, matrix.m31, matrix.m32);
 53819    }
 820
 821    /// <summary>
 822    /// Extracts the right direction from the 4x4 matrix.
 823    /// </summary>
 824    public static Vector3d ExtractRight(Fixed4x4 matrix)
 91825    {
 91826        return new Vector3d(matrix.m00, matrix.m01, matrix.m02).Normalize();
 91827    }
 828
 829    /// <summary>
 830    /// Extracts the up direction from the 4x4 matrix.
 831    /// </summary>
 832    /// <remarks>
 833    /// This is the surface normal if the matrix represents ground orientation.
 834    /// </remarks>
 835    /// <param name="matrix"></param>
 836    /// <returns>A <see cref="Vector3d"/> representing the up direction.</returns>
 837    public static Vector3d ExtractUp(Fixed4x4 matrix)
 91838    {
 91839        return new Vector3d(matrix.m10, matrix.m11, matrix.m12).Normalize();
 91840    }
 841
 842    /// <summary>
 843    /// Extracts the forward direction from the 4x4 matrix.
 844    /// </summary>
 845    public static Vector3d ExtractForward(Fixed4x4 matrix)
 91846    {
 91847        return new Vector3d(matrix.m20, matrix.m21, matrix.m22).Normalize();
 91848    }
 849
 850    /// <summary>
 851    /// Extracts the scaling factors from the matrix by calculating the magnitudes of the basis vectors (non-lossy).
 852    /// </summary>
 853    /// <returns>A Vector3d representing the precise scale along the X, Y, and Z axes.</returns>
 854    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 855    public static Vector3d ExtractScale(Fixed4x4 matrix)
 108856    {
 108857        return new Vector3d(
 108858            new Vector3d(matrix.m00, matrix.m01, matrix.m02).Magnitude,  // X scale
 108859            new Vector3d(matrix.m10, matrix.m11, matrix.m12).Magnitude,  // Y scale
 108860            new Vector3d(matrix.m20, matrix.m21, matrix.m22).Magnitude   // Z scale
 108861        );
 108862    }
 863
 864    /// <summary>
 865    /// Extracts the scaling factors from the matrix by returning the diagonal elements (lossy).
 866    /// </summary>
 867    /// <returns>A Vector3d representing the scale along X, Y, and Z axes (lossy).</returns>
 868    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 869    public static Vector3d ExtractLossyScale(Fixed4x4 matrix)
 3870    {
 3871        return new Vector3d(matrix.m00, matrix.m11, matrix.m22);
 3872    }
 873
 874    /// <summary>
 875    /// Extracts the rotation component from the 4x4 matrix by normalizing the rotation matrix.
 876    /// </summary>
 877    /// <param name="matrix">The matrix from which to extract the rotation.</param>
 878    /// <returns>A FixedQuaternion representing the rotation component.</returns>
 879    public static FixedQuaternion ExtractRotation(Fixed4x4 matrix)
 51880    {
 51881        Vector3d scale = ExtractScale(matrix);
 882
 883        // prevent divide by zero exception
 51884        Fixed64 scaleX = scale.x == Fixed64.Zero ? Fixed64.One : scale.x;
 51885        Fixed64 scaleY = scale.y == Fixed64.Zero ? Fixed64.One : scale.y;
 51886        Fixed64 scaleZ = scale.z == Fixed64.Zero ? Fixed64.One : scale.z;
 887
 51888        Fixed4x4 normalizedMatrix = new(
 51889            matrix.m00 / scaleX, matrix.m01 / scaleX, matrix.m02 / scaleX, Fixed64.Zero,
 51890            matrix.m10 / scaleY, matrix.m11 / scaleY, matrix.m12 / scaleY, Fixed64.Zero,
 51891            matrix.m20 / scaleZ, matrix.m21 / scaleZ, matrix.m22 / scaleZ, Fixed64.Zero,
 51892            Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One
 51893        );
 894
 51895        return FixedQuaternion.FromMatrix(normalizedMatrix);
 51896    }
 897
 898    /// <summary>
 899    /// Decomposes a 4x4 matrix into its translation, scale, and rotation components.
 900    /// </summary>
 901    /// <param name="matrix">The 4x4 matrix to decompose.</param>
 902    /// <param name="scale">The extracted scale component.</param>
 903    /// <param name="rotation">The extracted rotation component as a quaternion.</param>
 904    /// <param name="translation">The extracted translation component.</param>
 905    /// <returns>True if decomposition was successful, otherwise false.</returns>
 906    public static bool Decompose(
 907        Fixed4x4 matrix,
 908        out Vector3d scale,
 909        out FixedQuaternion rotation,
 910        out Vector3d translation)
 4911    {
 912        // Extract scale by calculating the magnitudes of the basis vectors
 4913        scale = ExtractScale(matrix);
 914
 915        // prevent divide by zero exception
 4916        scale = new Vector3d(
 4917             scale.x == Fixed64.Zero ? Fixed64.One : scale.x,
 4918             scale.y == Fixed64.Zero ? Fixed64.One : scale.y,
 4919             scale.z == Fixed64.Zero ? Fixed64.One : scale.z);
 920
 921        // normalize rotation and scaling
 4922        Fixed4x4 normalizedMatrix = ApplyScaleToRotation(matrix, Vector3d.One / scale);
 923
 924        // Extract translation
 4925        translation = new Vector3d(normalizedMatrix.m30, normalizedMatrix.m31, normalizedMatrix.m32);
 926
 927        // Check the determinant to ensure correct handedness
 4928        Fixed64 determinant = normalizedMatrix.GetDeterminant();
 4929        if (determinant < Fixed64.Zero)
 1930        {
 931            // Adjust for left-handed coordinate system by flipping one of the axes
 1932            scale.x = -scale.x;
 1933            normalizedMatrix.m00 = -normalizedMatrix.m00;
 1934            normalizedMatrix.m01 = -normalizedMatrix.m01;
 1935            normalizedMatrix.m02 = -normalizedMatrix.m02;
 1936        }
 937
 938        // Extract the rotation component from the orthogonalized matrix
 4939        rotation = FixedQuaternion.FromMatrix(normalizedMatrix);
 940
 4941        return true;
 4942    }
 943
 944    /// <summary>
 945    /// Sets the translation component of the 4x4 matrix.
 946    /// </summary>
 947    /// <param name="matrix">The matrix to modify.</param>
 948    /// <param name="translation">The new translation vector.</param>
 949    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 950    public static Fixed4x4 SetTranslation(Fixed4x4 matrix, Vector3d translation)
 11951    {
 11952        matrix.m30 = translation.x;
 11953        matrix.m31 = translation.y;
 11954        matrix.m32 = translation.z;
 11955        return matrix;
 11956    }
 957
 958    /// <summary>
 959    /// Sets the scale component of the 4x4 matrix by assigning the provided scale vector to the matrix's diagonal eleme
 960    /// </summary>
 961    /// <param name="matrix">The matrix to modify. Typically an identity or transformation matrix.</param>
 962    /// <param name="scale">The new scale vector to apply along the X, Y, and Z axes.</param>
 963    /// <remarks>
 964    /// Best used for applying scale to an identity matrix or resetting the scale on an existing matrix.
 965    /// For non-uniform scaling in combination with rotation, use <see cref="ApplyScaleToRotation"/>.
 966    /// </remarks>
 967    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 968    public static Fixed4x4 SetScale(Fixed4x4 matrix, Vector3d scale)
 1969    {
 1970        matrix.m00 = scale.x;
 1971        matrix.m11 = scale.y;
 1972        matrix.m22 = scale.z;
 1973        return matrix;
 1974    }
 975
 976    /// <summary>
 977    /// Applies non-uniform scaling to the 4x4 matrix by multiplying the scale vector with the rotation matrix's basis v
 978    /// </summary>
 979    /// <param name="matrix">The matrix to modify. Should already contain a valid rotation component.</param>
 980    /// <param name="scale">The scale vector to apply along the X, Y, and Z axes.</param>
 981    /// <remarks>
 982    /// Use this method when scaling is required in combination with an existing rotation, ensuring proper axis alignmen
 983    /// </remarks>
 984    public static Fixed4x4 ApplyScaleToRotation(Fixed4x4 matrix, Vector3d scale)
 16985    {
 986        // Scale each row of the rotation matrix
 16987        matrix.m00 *= scale.x;
 16988        matrix.m01 *= scale.x;
 16989        matrix.m02 *= scale.x;
 990
 16991        matrix.m10 *= scale.y;
 16992        matrix.m11 *= scale.y;
 16993        matrix.m12 *= scale.y;
 994
 16995        matrix.m20 *= scale.z;
 16996        matrix.m21 *= scale.z;
 16997        matrix.m22 *= scale.z;
 998
 16999        return matrix;
 161000    }
 1001
 1002    /// <summary>
 1003    /// Resets the scaling part of the matrix to identity (1,1,1).
 1004    /// </summary>
 1005    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1006    public static Fixed4x4 ResetScaleToIdentity(Fixed4x4 matrix)
 21007    {
 21008        matrix.m00 = Fixed64.One;  // X scale
 21009        matrix.m11 = Fixed64.One;  // Y scale
 21010        matrix.m22 = Fixed64.One;  // Z scale
 1011
 21012        return matrix;
 21013    }
 1014
 1015    /// <summary>
 1016    /// Sets the global scale of an object using a 4x4 transformation matrix.
 1017    /// </summary>
 1018    /// <param name="matrix">The transformation matrix representing the object's global state.</param>
 1019    /// <param name="globalScale">The desired global scale as a vector.</param>
 1020    /// <remarks>
 1021    /// The method extracts the current global scale from the matrix and computes the new local scale
 1022    /// by dividing the desired global scale by the current global scale.
 1023    /// The new local scale is then applied to the matrix.
 1024    /// </remarks>
 1025    public static Fixed4x4 SetGlobalScale(Fixed4x4 matrix, Vector3d globalScale)
 21026    {
 1027        // normalize the matrix to avoid drift in the rotation component
 21028        matrix = NormalizeRotationMatrix(matrix);
 1029
 1030        // Reset the local scaling portion of the matrix
 21031        matrix.ResetScaleToIdentity();
 1032
 1033        // Compute the new local scale by dividing the desired global scale by the current scale (which was reset to (1,
 21034        Vector3d newLocalScale = new(
 21035           globalScale.x / Fixed64.One,
 21036           globalScale.y / Fixed64.One,
 21037           globalScale.z / Fixed64.One
 21038        );
 1039
 1040        // Apply the new local scale directly to the matrix
 21041        return ApplyScaleToRotation(matrix, newLocalScale);
 21042    }
 1043
 1044    /// <summary>
 1045    /// Replaces the rotation component of the 4x4 matrix using the provided quaternion, without affecting the translati
 1046    /// </summary>
 1047    /// <param name="matrix">The matrix to modify. The rotation will replace the upper-left 3x3 portion of the matrix.</
 1048    /// <param name="rotation">The quaternion representing the new rotation to apply.</param>
 1049    /// <remarks>
 1050    /// This method preserves the matrix's translation component. For complete transformation updates, use <see cref="Se
 1051    /// </remarks>
 1052    public static Fixed4x4 SetRotation(Fixed4x4 matrix, FixedQuaternion rotation)
 21053    {
 21054        Fixed3x3 rotationMatrix = rotation.ToMatrix3x3();
 1055
 21056        Vector3d scale = ExtractScale(matrix);
 1057
 1058        // Apply rotation to the upper-left 3x3 matrix
 1059
 21060        matrix.m00 = rotationMatrix.m00 * scale.x;
 21061        matrix.m01 = rotationMatrix.m01 * scale.x;
 21062        matrix.m02 = rotationMatrix.m02 * scale.x;
 1063
 21064        matrix.m10 = rotationMatrix.m10 * scale.y;
 21065        matrix.m11 = rotationMatrix.m11 * scale.y;
 21066        matrix.m12 = rotationMatrix.m12 * scale.y;
 1067
 21068        matrix.m20 = rotationMatrix.m20 * scale.z;
 21069        matrix.m21 = rotationMatrix.m21 * scale.z;
 21070        matrix.m22 = rotationMatrix.m22 * scale.z;
 1071
 21072        return matrix;
 21073    }
 1074
 1075    /// <summary>
 1076    /// Normalizes the rotation component of a 4x4 matrix by ensuring the basis vectors are orthogonal and unit length.
 1077    /// </summary>
 1078    /// <remarks>
 1079    /// This method recalculates the X, Y, and Z basis vectors from the upper-left 3x3 portion of the matrix, ensuring t
 1080    /// The remaining components of the matrix are reset to maintain a valid transformation structure.
 1081    ///
 1082    /// Use Cases:
 1083    /// - Ensuring the rotation component remains stable and accurate after multiple transformations.
 1084    /// - Used in 3D transformations to prevent numerical drift from affecting the orientation over time.
 1085    /// - Essential for cases where precise orientation is required, such as animations or physics simulations.
 1086    /// </remarks>
 1087    public static Fixed4x4 NormalizeRotationMatrix(Fixed4x4 matrix)
 131088    {
 131089        Vector3d basisX = new Vector3d(matrix.m00, matrix.m01, matrix.m02).Normalize();
 131090        Vector3d basisY = new Vector3d(matrix.m10, matrix.m11, matrix.m12).Normalize();
 131091        Vector3d basisZ = new Vector3d(matrix.m20, matrix.m21, matrix.m22).Normalize();
 1092
 131093        return new Fixed4x4(
 131094            basisX.x, basisX.y, basisX.z, Fixed64.Zero,
 131095            basisY.x, basisY.y, basisY.z, Fixed64.Zero,
 131096            basisZ.x, basisZ.y, basisZ.z, Fixed64.Zero,
 131097            Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One
 131098        );
 131099    }
 1100
 1101    #endregion
 1102
 1103    #region Static Matrix Operators
 1104
 1105    /// <summary>
 1106    /// Linearly interpolates between two matrices component-wise.
 1107    /// </summary>
 1108    public static Fixed4x4 Lerp(Fixed4x4 a, Fixed4x4 b, Fixed64 t)
 11109    {
 11110        return new Fixed4x4(
 11111            FixedMath.LinearInterpolate(a.m00, b.m00, t),
 11112            FixedMath.LinearInterpolate(a.m01, b.m01, t),
 11113            FixedMath.LinearInterpolate(a.m02, b.m02, t),
 11114            FixedMath.LinearInterpolate(a.m03, b.m03, t),
 11115            FixedMath.LinearInterpolate(a.m10, b.m10, t),
 11116            FixedMath.LinearInterpolate(a.m11, b.m11, t),
 11117            FixedMath.LinearInterpolate(a.m12, b.m12, t),
 11118            FixedMath.LinearInterpolate(a.m13, b.m13, t),
 11119            FixedMath.LinearInterpolate(a.m20, b.m20, t),
 11120            FixedMath.LinearInterpolate(a.m21, b.m21, t),
 11121            FixedMath.LinearInterpolate(a.m22, b.m22, t),
 11122            FixedMath.LinearInterpolate(a.m23, b.m23, t),
 11123            FixedMath.LinearInterpolate(a.m30, b.m30, t),
 11124            FixedMath.LinearInterpolate(a.m31, b.m31, t),
 11125            FixedMath.LinearInterpolate(a.m32, b.m32, t),
 11126            FixedMath.LinearInterpolate(a.m33, b.m33, t));
 11127    }
 1128
 1129    /// <summary>
 1130    /// Transposes the matrix by swapping rows and columns.
 1131    /// </summary>
 1132    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1133    public static Fixed4x4 Transpose(Fixed4x4 matrix)
 11134    {
 11135        return new Fixed4x4(
 11136            matrix.m00, matrix.m10, matrix.m20, matrix.m30,
 11137            matrix.m01, matrix.m11, matrix.m21, matrix.m31,
 11138            matrix.m02, matrix.m12, matrix.m22, matrix.m32,
 11139            matrix.m03, matrix.m13, matrix.m23, matrix.m33);
 11140    }
 1141
 1142    /// <summary>
 1143    /// Divides each component of one matrix by the corresponding component of another matrix.
 1144    /// </summary>
 1145    /// <exception cref="DivideByZeroException">
 1146    /// Thrown when any divisor component is zero.
 1147    /// </exception>
 1148    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1149    public static Fixed4x4 ComponentDivide(Fixed4x4 dividend, Fixed4x4 divisor)
 21150    {
 21151        return new Fixed4x4(
 21152            dividend.m00 / divisor.m00,
 21153            dividend.m01 / divisor.m01,
 21154            dividend.m02 / divisor.m02,
 21155            dividend.m03 / divisor.m03,
 21156            dividend.m10 / divisor.m10,
 21157            dividend.m11 / divisor.m11,
 21158            dividend.m12 / divisor.m12,
 21159            dividend.m13 / divisor.m13,
 21160            dividend.m20 / divisor.m20,
 21161            dividend.m21 / divisor.m21,
 21162            dividend.m22 / divisor.m22,
 21163            dividend.m23 / divisor.m23,
 21164            dividend.m30 / divisor.m30,
 21165            dividend.m31 / divisor.m31,
 21166            dividend.m32 / divisor.m32,
 21167            dividend.m33 / divisor.m33);
 11168    }
 1169
 1170    /// <summary>
 1171    /// Divides one matrix by another using inverse matrix division.
 1172    /// </summary>
 1173    /// <remarks>
 1174    /// This is equivalent to <c>dividend * Invert(divisor)</c>. Use <see cref="ComponentDivide"/>
 1175    /// when each matrix component should be divided by the corresponding component of another matrix.
 1176    /// </remarks>
 1177    /// <exception cref="InvalidOperationException">
 1178    /// Thrown when <paramref name="divisor"/> is not invertible.
 1179    /// </exception>
 1180    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1181    public static Fixed4x4 Divide(Fixed4x4 dividend, Fixed4x4 divisor)
 21182    {
 21183        if (!Invert(divisor, out Fixed4x4 inverseDivisor))
 11184            throw new InvalidOperationException("Matrix divisor is not invertible.");
 1185
 11186        return dividend * inverseDivisor;
 11187    }
 1188
 1189    /// <summary>
 1190    /// Inverts the matrix if it is invertible (i.e., if the determinant is not zero).
 1191    /// </summary>
 1192    /// <remarks>
 1193    /// To Invert a FixedMatrix4x4, we need to calculate the inverse for each element.
 1194    /// This involves computing the cofactor for each element,
 1195    /// which is the determinant of the submatrix when the row and column of that element are removed,
 1196    /// multiplied by a sign based on the element's position.
 1197    /// After computing all cofactors, the result is transposed to get the inverse matrix.
 1198    /// </remarks>
 1199    public static bool Invert(Fixed4x4 matrix, out Fixed4x4 result)
 131200    {
 131201        if (!matrix.IsAffine)
 71202            return FullInvert(matrix, out result);
 1203
 61204        Fixed64 det = matrix.GetDeterminant();
 1205
 61206        if (det == Fixed64.Zero)
 11207        {
 11208            result = Identity;
 11209            return false;
 1210        }
 1211
 51212        Fixed64 invDet = Fixed64.One / det;
 1213
 1214        // Invert the 3×3 upper-left rotation/scale matrix
 51215        result = new Fixed4x4(
 51216            (matrix.m11 * matrix.m22 - matrix.m12 * matrix.m21) * invDet,
 51217            (matrix.m02 * matrix.m21 - matrix.m01 * matrix.m22) * invDet,
 51218            (matrix.m01 * matrix.m12 - matrix.m02 * matrix.m11) * invDet, Fixed64.Zero,
 51219
 51220            (matrix.m12 * matrix.m20 - matrix.m10 * matrix.m22) * invDet,
 51221            (matrix.m00 * matrix.m22 - matrix.m02 * matrix.m20) * invDet,
 51222            (matrix.m02 * matrix.m10 - matrix.m00 * matrix.m12) * invDet, Fixed64.Zero,
 51223
 51224            (matrix.m10 * matrix.m21 - matrix.m11 * matrix.m20) * invDet,
 51225            (matrix.m01 * matrix.m20 - matrix.m00 * matrix.m21) * invDet,
 51226            (matrix.m00 * matrix.m11 - matrix.m01 * matrix.m10) * invDet, Fixed64.Zero,
 51227
 51228            Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One  // Ensure homogeneous coordinate stays valid
 51229        );
 1230
 51231        Fixed3x3 rotationScaleInverse = new(
 51232            result.m00, result.m01, result.m02,
 51233            result.m10, result.m11, result.m12,
 51234            result.m20, result.m21, result.m22
 51235        );
 1236
 1237        // Correct translation component
 51238        Vector3d transformedTranslation = new(matrix.m30, matrix.m31, matrix.m32);
 51239        transformedTranslation = -Fixed3x3.TransformDirection(rotationScaleInverse, transformedTranslation);
 1240
 51241        result.m30 = transformedTranslation.x;
 51242        result.m31 = transformedTranslation.y;
 51243        result.m32 = transformedTranslation.z;
 51244        result.m33 = Fixed64.One;
 1245
 51246        return true;
 131247    }
 1248
 1249    private static bool FullInvert(Fixed4x4 matrix, out Fixed4x4 result)
 71250    {
 71251        Fixed64 det = matrix.GetDeterminant();
 1252
 71253        if (det == Fixed64.Zero)
 31254        {
 31255            result = Fixed4x4.Identity;
 31256            return false;
 1257        }
 1258
 41259        Fixed64 invDet = Fixed64.One / det;
 1260
 1261        // Inversion using cofactors and determinants of 3x3 submatrices
 41262        result = new Fixed4x4
 41263        {
 41264            // First row
 41265            m00 = invDet * ((matrix.m11 * matrix.m22 * matrix.m33 + matrix.m12 * matrix.m23 * matrix.m31 + matrix.m13 * 
 41266                          - (matrix.m13 * matrix.m22 * matrix.m31 + matrix.m11 * matrix.m23 * matrix.m32 + matrix.m12 * 
 41267            m01 = invDet * ((matrix.m01 * matrix.m23 * matrix.m32 + matrix.m02 * matrix.m21 * matrix.m33 + matrix.m03 * 
 41268                          - (matrix.m03 * matrix.m21 * matrix.m32 + matrix.m01 * matrix.m22 * matrix.m33 + matrix.m02 * 
 41269            m02 = invDet * ((matrix.m01 * matrix.m12 * matrix.m33 + matrix.m02 * matrix.m13 * matrix.m31 + matrix.m03 * 
 41270                          - (matrix.m03 * matrix.m12 * matrix.m31 + matrix.m01 * matrix.m13 * matrix.m32 + matrix.m02 * 
 41271            m03 = invDet * ((matrix.m01 * matrix.m13 * matrix.m22 + matrix.m02 * matrix.m11 * matrix.m23 + matrix.m03 * 
 41272                          - (matrix.m03 * matrix.m11 * matrix.m22 + matrix.m01 * matrix.m12 * matrix.m23 + matrix.m02 * 
 41273
 41274            // Second row
 41275            m10 = invDet * ((matrix.m10 * matrix.m23 * matrix.m32 + matrix.m12 * matrix.m20 * matrix.m33 + matrix.m13 * 
 41276                          - (matrix.m13 * matrix.m20 * matrix.m32 + matrix.m10 * matrix.m22 * matrix.m33 + matrix.m12 * 
 41277            m11 = invDet * ((matrix.m00 * matrix.m22 * matrix.m33 + matrix.m02 * matrix.m23 * matrix.m30 + matrix.m03 * 
 41278                          - (matrix.m03 * matrix.m20 * matrix.m32 + matrix.m00 * matrix.m23 * matrix.m32 + matrix.m02 * 
 41279            m12 = invDet * ((matrix.m00 * matrix.m13 * matrix.m32 + matrix.m02 * matrix.m10 * matrix.m33 + matrix.m03 * 
 41280                          - (matrix.m03 * matrix.m10 * matrix.m32 + matrix.m00 * matrix.m12 * matrix.m33 + matrix.m02 * 
 41281            m13 = invDet * ((matrix.m00 * matrix.m12 * matrix.m23 + matrix.m02 * matrix.m13 * matrix.m20 + matrix.m03 * 
 41282                          - (matrix.m03 * matrix.m10 * matrix.m22 + matrix.m00 * matrix.m13 * matrix.m22 + matrix.m02 * 
 41283
 41284            // Third row
 41285            m20 = invDet * ((matrix.m10 * matrix.m21 * matrix.m33 + matrix.m11 * matrix.m23 * matrix.m30 + matrix.m13 * 
 41286                          - (matrix.m13 * matrix.m20 * matrix.m31 + matrix.m10 * matrix.m23 * matrix.m31 + matrix.m11 * 
 41287            m21 = invDet * ((matrix.m00 * matrix.m23 * matrix.m31 + matrix.m01 * matrix.m20 * matrix.m33 + matrix.m03 * 
 41288                          - (matrix.m03 * matrix.m20 * matrix.m31 + matrix.m00 * matrix.m21 * matrix.m33 + matrix.m01 * 
 41289            m22 = invDet * ((matrix.m00 * matrix.m11 * matrix.m33 + matrix.m01 * matrix.m13 * matrix.m30 + matrix.m03 * 
 41290                          - (matrix.m03 * matrix.m10 * matrix.m31 + matrix.m00 * matrix.m13 * matrix.m31 + matrix.m01 * 
 41291            m23 = invDet * ((matrix.m00 * matrix.m13 * matrix.m21 + matrix.m01 * matrix.m10 * matrix.m23 + matrix.m03 * 
 41292                          - (matrix.m03 * matrix.m10 * matrix.m21 + matrix.m00 * matrix.m11 * matrix.m23 + matrix.m01 * 
 41293
 41294            // Fourth row
 41295            m30 = invDet * ((matrix.m10 * matrix.m22 * matrix.m31 + matrix.m11 * matrix.m20 * matrix.m32 + matrix.m12 * 
 41296                          - (matrix.m12 * matrix.m20 * matrix.m31 + matrix.m10 * matrix.m21 * matrix.m32 + matrix.m11 * 
 41297            m31 = invDet * ((matrix.m00 * matrix.m21 * matrix.m32 + matrix.m01 * matrix.m22 * matrix.m30 + matrix.m02 * 
 41298                          - (matrix.m02 * matrix.m20 * matrix.m31 + matrix.m00 * matrix.m22 * matrix.m31 + matrix.m01 * 
 41299            m32 = invDet * ((matrix.m00 * matrix.m12 * matrix.m31 + matrix.m01 * matrix.m10 * matrix.m32 + matrix.m02 * 
 41300                          - (matrix.m02 * matrix.m10 * matrix.m31 + matrix.m00 * matrix.m11 * matrix.m32 + matrix.m01 * 
 41301            m33 = invDet * ((matrix.m00 * matrix.m11 * matrix.m22 + matrix.m01 * matrix.m12 * matrix.m20 + matrix.m02 * 
 41302                          - (matrix.m02 * matrix.m10 * matrix.m21 + matrix.m00 * matrix.m12 * matrix.m21 + matrix.m01 * 
 41303        };
 1304
 41305        return true;
 71306    }
 1307
 1308    /// <summary>
 1309    /// Transforms a 4D vector by a 4x4 matrix, preserving the computed W component.
 1310    /// </summary>
 1311    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1312    public static Vector4d Transform(Fixed4x4 matrix, Vector4d vector)
 21313    {
 21314        return Vector4d.Transform(matrix, vector);
 21315    }
 1316
 1317    /// <summary>
 1318    /// Transforms a point from local space to world space using this transformation matrix.
 1319    /// </summary>
 1320    /// <remarks>
 1321    /// This is the same as doing `<see cref="Fixed4x4"/> a * <see cref="Vector3d"/> b`
 1322    /// </remarks>
 1323    /// <param name="matrix">The transformation matrix.</param>
 1324    /// <param name="point">The local-space point.</param>
 1325    /// <returns>The transformed point in world space.</returns>
 1326    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1327    public static Vector3d TransformPoint(Fixed4x4 matrix, Vector3d point)
 131328    {
 131329        if (matrix.IsAffine)
 81330        {
 81331            return new Vector3d(
 81332                matrix.m00 * point.x + matrix.m01 * point.y + matrix.m02 * point.z + matrix.m30,
 81333                matrix.m10 * point.x + matrix.m11 * point.y + matrix.m12 * point.z + matrix.m31,
 81334                matrix.m20 * point.x + matrix.m21 * point.y + matrix.m22 * point.z + matrix.m32
 81335            );
 1336        }
 1337
 51338        return FullTransformPoint(matrix, point);
 131339    }
 1340
 1341    private static Vector3d FullTransformPoint(Fixed4x4 matrix, Vector3d point)
 51342    {
 1343        // Full 4×4 transformation (needed for perspective projections)
 51344        Fixed64 w = matrix.m03 * point.x + matrix.m13 * point.y + matrix.m23 * point.z + matrix.m33;
 61345        if (w == Fixed64.Zero) w = Fixed64.One;  // Prevent divide-by-zero
 1346
 51347        return new Vector3d(
 51348            (matrix.m00 * point.x + matrix.m01 * point.y + matrix.m02 * point.z + matrix.m30) / w,
 51349            (matrix.m10 * point.x + matrix.m11 * point.y + matrix.m12 * point.z + matrix.m31) / w,
 51350            (matrix.m20 * point.x + matrix.m21 * point.y + matrix.m22 * point.z + matrix.m32) / w
 51351        );
 51352    }
 1353
 1354    /// <summary>
 1355    /// Transforms a point from world space into the local space of the matrix.
 1356    /// </summary>
 1357    /// <param name="matrix">The transformation matrix.</param>
 1358    /// <param name="point">The world-space point.</param>
 1359    /// <returns>The local-space point relative to the transformation matrix.</returns>
 1360    public static Vector3d InverseTransformPoint(Fixed4x4 matrix, Vector3d point)
 71361    {
 1362        // Invert the transformation matrix
 71363        if (!Invert(matrix, out Fixed4x4 inverseMatrix))
 11364            throw new InvalidOperationException("Matrix is not invertible.");
 1365
 61366        if (inverseMatrix.IsAffine)
 31367        {
 31368            return new Vector3d(
 31369                inverseMatrix.m00 * point.x + inverseMatrix.m01 * point.y + inverseMatrix.m02 * point.z + inverseMatrix.
 31370                inverseMatrix.m10 * point.x + inverseMatrix.m11 * point.y + inverseMatrix.m12 * point.z + inverseMatrix.
 31371                inverseMatrix.m20 * point.x + inverseMatrix.m21 * point.y + inverseMatrix.m22 * point.z + inverseMatrix.
 31372            );
 1373        }
 1374
 31375        return FullInverseTransformPoint(inverseMatrix, point);
 61376    }
 1377
 1378    private static Vector3d FullInverseTransformPoint(Fixed4x4 matrix, Vector3d point)
 31379    {
 1380        // Full 4×4 transformation (needed for perspective projections)
 31381        Fixed64 w = matrix.m03 * point.x + matrix.m13 * point.y + matrix.m23 * point.z + matrix.m33;
 41382        if (w == Fixed64.Zero) w = Fixed64.One;  // Prevent divide-by-zero
 1383
 31384        return new Vector3d(
 31385            (matrix.m00 * point.x + matrix.m01 * point.y + matrix.m02 * point.z + matrix.m30) / w,
 31386            (matrix.m10 * point.x + matrix.m11 * point.y + matrix.m12 * point.z + matrix.m31) / w,
 31387            (matrix.m20 * point.x + matrix.m21 * point.y + matrix.m22 * point.z + matrix.m32) / w
 31388        );
 31389    }
 1390
 1391    #endregion
 1392
 1393    #region Operators
 1394
 1395    /// <summary>
 1396    /// Negates the specified matrix by multiplying all its values by -1.
 1397    /// </summary>
 1398    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1399    public static Fixed4x4 operator -(Fixed4x4 value)
 11400    {
 11401        Fixed4x4 result = default;
 11402        result.m00 = -value.m00;
 11403        result.m01 = -value.m01;
 11404        result.m02 = -value.m02;
 11405        result.m03 = -value.m03;
 11406        result.m10 = -value.m10;
 11407        result.m11 = -value.m11;
 11408        result.m12 = -value.m12;
 11409        result.m13 = -value.m13;
 11410        result.m20 = -value.m20;
 11411        result.m21 = -value.m21;
 11412        result.m22 = -value.m22;
 11413        result.m23 = -value.m23;
 11414        result.m30 = -value.m30;
 11415        result.m31 = -value.m31;
 11416        result.m32 = -value.m32;
 11417        result.m33 = -value.m33;
 11418        return result;
 11419    }
 1420
 1421    /// <summary>
 1422    /// Adds two matrices element-wise.
 1423    /// </summary>
 1424    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1425    public static Fixed4x4 operator +(Fixed4x4 lhs, Fixed4x4 rhs)
 31426    {
 31427        return new Fixed4x4(
 31428            lhs.m00 + rhs.m00, lhs.m01 + rhs.m01, lhs.m02 + rhs.m02, lhs.m03 + rhs.m03,
 31429            lhs.m10 + rhs.m10, lhs.m11 + rhs.m11, lhs.m12 + rhs.m12, lhs.m13 + rhs.m13,
 31430            lhs.m20 + rhs.m20, lhs.m21 + rhs.m21, lhs.m22 + rhs.m22, lhs.m23 + rhs.m23,
 31431            lhs.m30 + rhs.m30, lhs.m31 + rhs.m31, lhs.m32 + rhs.m32, lhs.m33 + rhs.m33);
 31432    }
 1433
 1434    /// <summary>
 1435    /// Subtracts two matrices element-wise.
 1436    /// </summary>
 1437    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1438    public static Fixed4x4 operator -(Fixed4x4 lhs, Fixed4x4 rhs)
 11439    {
 11440        return new Fixed4x4(
 11441            lhs.m00 - rhs.m00, lhs.m01 - rhs.m01, lhs.m02 - rhs.m02, lhs.m03 - rhs.m03,
 11442            lhs.m10 - rhs.m10, lhs.m11 - rhs.m11, lhs.m12 - rhs.m12, lhs.m13 - rhs.m13,
 11443            lhs.m20 - rhs.m20, lhs.m21 - rhs.m21, lhs.m22 - rhs.m22, lhs.m23 - rhs.m23,
 11444            lhs.m30 - rhs.m30, lhs.m31 - rhs.m31, lhs.m32 - rhs.m32, lhs.m33 - rhs.m33);
 11445    }
 1446
 1447    /// <summary>
 1448    /// Multiplies two 4x4 matrices using standard matrix multiplication.
 1449    /// </summary>
 1450    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1451    public static Fixed4x4 operator *(Fixed4x4 lhs, Fixed4x4 rhs)
 231452    {
 231453        if (lhs.IsAffine && rhs.IsAffine)
 211454        {
 1455            // Optimized affine multiplication (skips full 4×4 multiplication)
 211456            return new Fixed4x4(
 211457                lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10 + lhs.m02 * rhs.m20,
 211458                lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11 + lhs.m02 * rhs.m21,
 211459                lhs.m00 * rhs.m02 + lhs.m01 * rhs.m12 + lhs.m02 * rhs.m22,
 211460                Fixed64.Zero,
 211461
 211462                lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10 + lhs.m12 * rhs.m20,
 211463                lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21,
 211464                lhs.m10 * rhs.m02 + lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22,
 211465                Fixed64.Zero,
 211466
 211467                lhs.m20 * rhs.m00 + lhs.m21 * rhs.m10 + lhs.m22 * rhs.m20,
 211468                lhs.m20 * rhs.m01 + lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21,
 211469                lhs.m20 * rhs.m02 + lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22,
 211470                Fixed64.Zero,
 211471
 211472                lhs.m30 * rhs.m00 + lhs.m31 * rhs.m10 + lhs.m32 * rhs.m20 + rhs.m30,
 211473                lhs.m30 * rhs.m01 + lhs.m31 * rhs.m11 + lhs.m32 * rhs.m21 + rhs.m31,
 211474                lhs.m30 * rhs.m02 + lhs.m31 * rhs.m12 + lhs.m32 * rhs.m22 + rhs.m32,
 211475                Fixed64.One
 211476            );
 1477        }
 1478
 1479        // Full 4×4 multiplication (fallback for perspective matrices)
 21480        return new Fixed4x4(
 21481            // Upper-left 3×3 matrix multiplication (rotation & scale)
 21482            lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10 + lhs.m02 * rhs.m20 + lhs.m03 * rhs.m30,
 21483            lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11 + lhs.m02 * rhs.m21 + lhs.m03 * rhs.m31,
 21484            lhs.m00 * rhs.m02 + lhs.m01 * rhs.m12 + lhs.m02 * rhs.m22 + lhs.m03 * rhs.m32,
 21485            lhs.m00 * rhs.m03 + lhs.m01 * rhs.m13 + lhs.m02 * rhs.m23 + lhs.m03 * rhs.m33,
 21486
 21487            lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10 + lhs.m12 * rhs.m20 + lhs.m13 * rhs.m30,
 21488            lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21 + lhs.m13 * rhs.m31,
 21489            lhs.m10 * rhs.m02 + lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22 + lhs.m13 * rhs.m32,
 21490            lhs.m10 * rhs.m03 + lhs.m11 * rhs.m13 + lhs.m12 * rhs.m23 + lhs.m13 * rhs.m33,
 21491
 21492            lhs.m20 * rhs.m00 + lhs.m21 * rhs.m10 + lhs.m22 * rhs.m20 + lhs.m23 * rhs.m30,
 21493            lhs.m20 * rhs.m01 + lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21 + lhs.m23 * rhs.m31,
 21494            lhs.m20 * rhs.m02 + lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22 + lhs.m23 * rhs.m32,
 21495            lhs.m20 * rhs.m03 + lhs.m21 * rhs.m13 + lhs.m22 * rhs.m23 + lhs.m23 * rhs.m33,
 21496
 21497            // Compute new translation
 21498            lhs.m30 * rhs.m00 + lhs.m31 * rhs.m10 + lhs.m32 * rhs.m20 + lhs.m33 * rhs.m30,
 21499            lhs.m30 * rhs.m01 + lhs.m31 * rhs.m11 + lhs.m32 * rhs.m21 + lhs.m33 * rhs.m31,
 21500            lhs.m30 * rhs.m02 + lhs.m31 * rhs.m12 + lhs.m32 * rhs.m22 + lhs.m33 * rhs.m32,
 21501            lhs.m30 * rhs.m03 + lhs.m31 * rhs.m13 + lhs.m32 * rhs.m23 + lhs.m33 * rhs.m33
 21502        );
 231503    }
 1504
 1505    /// <summary>
 1506    /// Multiplies every matrix component by a scalar.
 1507    /// </summary>
 1508    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1509    public static Fixed4x4 operator *(Fixed4x4 matrix, Fixed64 scalar)
 51510    {
 51511        return new Fixed4x4(
 51512            matrix.m00 * scalar, matrix.m01 * scalar, matrix.m02 * scalar, matrix.m03 * scalar,
 51513            matrix.m10 * scalar, matrix.m11 * scalar, matrix.m12 * scalar, matrix.m13 * scalar,
 51514            matrix.m20 * scalar, matrix.m21 * scalar, matrix.m22 * scalar, matrix.m23 * scalar,
 51515            matrix.m30 * scalar, matrix.m31 * scalar, matrix.m32 * scalar, matrix.m33 * scalar);
 51516    }
 1517
 1518    /// <summary>
 1519    /// Multiplies every matrix component by a scalar.
 1520    /// </summary>
 1521    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1522    public static Fixed4x4 operator *(Fixed64 scalar, Fixed4x4 matrix)
 11523    {
 11524        return matrix * scalar;
 11525    }
 1526
 1527    /// <summary>
 1528    /// Divides every matrix component by a scalar.
 1529    /// </summary>
 1530    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1531    public static Fixed4x4 operator /(Fixed4x4 matrix, Fixed64 scalar)
 21532    {
 21533        Fixed64 inverse = Fixed64.One / scalar;
 21534        return matrix * inverse;
 21535    }
 1536
 1537    /// <summary>
 1538    /// Determines whether two Fixed4x4 instances are equal.
 1539    /// </summary>
 1540    /// <param name="left">The first Fixed4x4 instance to compare.</param>
 1541    /// <param name="right">The second Fixed4x4 instance to compare.</param>
 1542    /// <returns>true if the specified Fixed4x4 instances are equal; otherwise, false.</returns>
 1543    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1544    public static bool operator ==(Fixed4x4 left, Fixed4x4 right)
 21545    {
 21546        return left.Equals(right);
 21547    }
 1548
 1549    /// <summary>
 1550    /// Determines whether two Fixed4x4 instances are not equal.
 1551    /// </summary>
 1552    /// <param name="left">The first Fixed4x4 instance to compare.</param>
 1553    /// <param name="right">The second Fixed4x4 instance to compare.</param>
 1554    /// <returns>true if the specified Fixed4x4 instances are not equal; otherwise, false.</returns>
 1555    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1556    public static bool operator !=(Fixed4x4 left, Fixed4x4 right)
 11557    {
 11558        return !(left == right);
 11559    }
 1560
 1561    #endregion
 1562
 1563    #region Equality and HashCode Overrides
 1564
 1565    /// <inheritdoc/>
 1566    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1567    public override bool Equals(object? obj)
 51568    {
 51569        return obj is Fixed4x4 x && Equals(x);
 51570    }
 1571
 1572    /// <inheritdoc/>
 1573    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1574    public bool Equals(Fixed4x4 other)
 321575    {
 321576        return m00 == other.m00 && m01 == other.m01 && m02 == other.m02 && m03 == other.m03 &&
 321577               m10 == other.m10 && m11 == other.m11 && m12 == other.m12 && m13 == other.m13 &&
 321578               m20 == other.m20 && m21 == other.m21 && m22 == other.m22 && m23 == other.m23 &&
 321579               m30 == other.m30 && m31 == other.m31 && m32 == other.m32 && m33 == other.m33;
 321580    }
 1581
 1582    /// <inheritdoc/>
 1583    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1584    public override int GetHashCode()
 41585    {
 1586        unchecked
 41587        {
 41588            int hash = 17;
 41589            hash = hash * 23 + m00.GetHashCode();
 41590            hash = hash * 23 + m01.GetHashCode();
 41591            hash = hash * 23 + m02.GetHashCode();
 41592            hash = hash * 23 + m03.GetHashCode();
 41593            hash = hash * 23 + m10.GetHashCode();
 41594            hash = hash * 23 + m11.GetHashCode();
 41595            hash = hash * 23 + m12.GetHashCode();
 41596            hash = hash * 23 + m13.GetHashCode();
 41597            hash = hash * 23 + m20.GetHashCode();
 41598            hash = hash * 23 + m21.GetHashCode();
 41599            hash = hash * 23 + m22.GetHashCode();
 41600            hash = hash * 23 + m23.GetHashCode();
 41601            hash = hash * 23 + m30.GetHashCode();
 41602            hash = hash * 23 + m31.GetHashCode();
 41603            hash = hash * 23 + m32.GetHashCode();
 41604            hash = hash * 23 + m33.GetHashCode();
 41605            return hash;
 1606        }
 41607    }
 1608
 1609    #endregion
 1610
 1611    #region Private Helpers
 1612
 1613    private static Fixed4x4 FromRotationMatrix(Fixed3x3 matrix)
 31614    {
 31615        return new Fixed4x4(
 31616            matrix.m00, matrix.m01, matrix.m02, Fixed64.Zero,
 31617            matrix.m10, matrix.m11, matrix.m12, Fixed64.Zero,
 31618            matrix.m20, matrix.m21, matrix.m22, Fixed64.Zero,
 31619            Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One);
 31620    }
 1621
 1622    private static void ValidateDepthRange(Fixed64 nearPlaneDistance, Fixed64 farPlaneDistance)
 61623    {
 61624        if (nearPlaneDistance < Fixed64.Zero)
 21625            throw new ArgumentOutOfRangeException(nameof(nearPlaneDistance), nearPlaneDistance, "Near plane distance mus
 1626
 41627        if (farPlaneDistance <= nearPlaneDistance)
 21628            throw new ArgumentOutOfRangeException(nameof(farPlaneDistance), farPlaneDistance, "Far plane distance must b
 21629    }
 1630
 1631    private static void ValidatePerspectiveDepthRange(Fixed64 nearPlaneDistance, Fixed64 farPlaneDistance)
 101632    {
 101633        if (nearPlaneDistance <= Fixed64.Zero)
 31634            throw new ArgumentOutOfRangeException(nameof(nearPlaneDistance), nearPlaneDistance, "Near plane distance mus
 1635
 71636        if (farPlaneDistance <= nearPlaneDistance)
 31637            throw new ArgumentOutOfRangeException(nameof(farPlaneDistance), farPlaneDistance, "Far plane distance must b
 41638    }
 1639
 1640    #endregion
 1641}

Methods/Properties

.ctor(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
FromRows(FixedMathSharp.Vector4d,FixedMathSharp.Vector4d,FixedMathSharp.Vector4d,FixedMathSharp.Vector4d)
FromColumns(FixedMathSharp.Vector4d,FixedMathSharp.Vector4d,FixedMathSharp.Vector4d,FixedMathSharp.Vector4d)
get_IsAffine()
get_Translation()
get_Right()
get_Left()
get_Up()
get_Down()
get_Forward()
get_Backward()
get_Scale()
get_Rotation()
get_Item(System.Int32)
set_Item(System.Int32,FixedMathSharp.Fixed64)
GetDeterminant()
ResetScaleToIdentity()
SetTransform(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d)
CreateTranslation(FixedMathSharp.Vector3d)
CreateTranslation(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateRotation(FixedMathSharp.FixedQuaternion)
CreateRotationX(FixedMathSharp.Fixed64)
CreateRotationY(FixedMathSharp.Fixed64)
CreateRotationZ(FixedMathSharp.Fixed64)
CreateFromAxisAngle(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
CreateFromEulerAngles(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateScale(FixedMathSharp.Vector3d)
CreateScale(FixedMathSharp.Fixed64)
CreateScale(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateLookAt(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
CreateOrthographic(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateOrthographicOffCenter(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreatePerspective(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreatePerspectiveFieldOfView(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreatePerspectiveOffCenter(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CreateWorld(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
CreateTransform(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d)
ScaleRotateTranslate(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d)
TranslateRotateScale(FixedMathSharp.Vector3d,FixedMathSharp.FixedQuaternion,FixedMathSharp.Vector3d)
ExtractTranslation(FixedMathSharp.Fixed4x4)
ExtractRight(FixedMathSharp.Fixed4x4)
ExtractUp(FixedMathSharp.Fixed4x4)
ExtractForward(FixedMathSharp.Fixed4x4)
ExtractScale(FixedMathSharp.Fixed4x4)
ExtractLossyScale(FixedMathSharp.Fixed4x4)
ExtractRotation(FixedMathSharp.Fixed4x4)
Decompose(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d&,FixedMathSharp.FixedQuaternion&,FixedMathSharp.Vector3d&)
SetTranslation(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d)
SetScale(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d)
ApplyScaleToRotation(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d)
ResetScaleToIdentity(FixedMathSharp.Fixed4x4)
SetGlobalScale(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d)
SetRotation(FixedMathSharp.Fixed4x4,FixedMathSharp.FixedQuaternion)
NormalizeRotationMatrix(FixedMathSharp.Fixed4x4)
Lerp(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed64)
Transpose(FixedMathSharp.Fixed4x4)
ComponentDivide(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4)
Divide(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4)
Invert(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4&)
FullInvert(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4&)
Transform(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector4d)
TransformPoint(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d)
FullTransformPoint(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d)
InverseTransformPoint(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d)
FullInverseTransformPoint(FixedMathSharp.Fixed4x4,FixedMathSharp.Vector3d)
op_UnaryNegation(FixedMathSharp.Fixed4x4)
op_Addition(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4)
op_Subtraction(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4)
op_Multiply(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4)
op_Multiply(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed64)
op_Multiply(FixedMathSharp.Fixed64,FixedMathSharp.Fixed4x4)
op_Division(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed64)
op_Equality(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4)
op_Inequality(FixedMathSharp.Fixed4x4,FixedMathSharp.Fixed4x4)
Equals(System.Object)
Equals(FixedMathSharp.Fixed4x4)
GetHashCode()
FromRotationMatrix(FixedMathSharp.Fixed3x3)
ValidateDepthRange(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
ValidatePerspectiveDepthRange(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)