From fbf0723640437ca8b6232837fc42ee91bfe57e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E6=97=B6=E8=BF=90?= <157733603@qq.com> Date: Sun, 11 Oct 2026 04:29:08 +0800 Subject: [PATCH] Fix Matrix3(Matrix4) copy producing transposed linear transform Matrix4 indexes M[i,j] column-first in its vector operator (X' = M00*x + M10*y + ...), while Matrix3 indexes row-first (X' = M00*x + M01*y + ...). Copying the 3x3 block verbatim therefore produced the TRANSPOSE of the intended linear transform, so rotations applied via getWorldMatrix() ran with the wrong sign (arc start/end angles, text rotation, ellipse major axis, etc.). Transpose the 3x3 block in the Matrix3(Matrix4) constructor so that Matrix3 * XYZ applies the same linear map as Matrix4 * XYZ. --- CSMath/Matrix3.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/CSMath/Matrix3.cs b/CSMath/Matrix3.cs index 9e732d1..cfd9121 100644 --- a/CSMath/Matrix3.cs +++ b/CSMath/Matrix3.cs @@ -1,4 +1,4 @@ -using CSMath.Extensions; +using CSMath.Extensions; using System; using System.Text; using System.Threading; @@ -91,14 +91,21 @@ public Matrix3( public Matrix3(Matrix4 matrix) { + // [PATCH] Matrix4 stores Mij with the FIRST index as the column (its own remarks show + // row 0 = M00, M10, M20, M30 and its vector operator computes X' = M00*x + M10*y + ...), + // while Matrix3's operators treat the FIRST index as the row (X' = M00*x + M01*y + ...). + // A plain copy therefore made the 3x3 linear part the TRANSPOSE of the real transform: + // every rotation applied through getWorldMatrix() ran with the wrong sign (arc start/end + // angles, text rotation, ellipse major-axis, ...). Transpose on copy so that + // Matrix3 * XYZ applies exactly the same linear map as Matrix4 * XYZ. this.M00 = matrix.M00; - this.M01 = matrix.M01; - this.M02 = matrix.M02; - this.M10 = matrix.M10; + this.M01 = matrix.M10; + this.M02 = matrix.M20; + this.M10 = matrix.M01; this.M11 = matrix.M11; - this.M12 = matrix.M12; - this.M20 = matrix.M20; - this.M21 = matrix.M21; + this.M12 = matrix.M21; + this.M20 = matrix.M02; + this.M21 = matrix.M12; this.M22 = matrix.M22; }