std.matrix — Matrizen und Transformationen

import std.matrix;

Feste Matrizen und Vektoren in f64: Vec3, Vec4, Mat3, Mat4 — mit allem, was eine Transformationskette braucht: Verschieben, Drehen, Skalieren, Invertieren, Kameramatrix und Projektion.

Zurück zur Unit-Übersicht · std.coordinates · std.quat · std.linalg

Quelle: std/matrix.lyx


1. Speicherordnung

 
Row-major. Das Feld mRC ist Zeile R, Spalte C — m01 steht also in Zeile 0, Spalte 1. Ein Vektor wird als Spalte rechts multipliziert:

var v2: Vec4 := Mat4MulVec4(m, v);     // v' = M · v

Wer die Matrix an OpenGL weiterreicht, transponiert vorher (Mat4Transpose) oder übergibt sie mit gesetztem transpose-Flag — GL erwartet column-major. Das ist der Fehler, der ein Bild kippt, ohne dass eine Meldung erscheint.

Aus derselben Ordnung folgt die Reihenfolge beim Verketten: Mat4Mul(A, B) wendet erst B, dann A an — wie in der Mathematik.


2. Vec3 und Vec4

Bereich Funktionen
Bauen Vec3New, Vec3Zero, Vec3One, Vec3UnitX/Y/Z, Vec4New, Vec4Zero
Rechnen Vec3Add, Vec3Sub, Vec3Neg, Vec3Scale, Vec3MulComp, Vec3Dot, Vec3Cross (Vec4 entsprechend)
Länge Vec3Length, Vec3LengthSq, Vec3Normalize, Vec3Distance, Vec3DistanceSq
Mischen Vec3Lerp, Vec3Reflect, Vec3Equals(a, b, eps)
3 ↔ 4 Vec4FromPoint (w = 1), Vec4FromDirection (w = 0), Vec4XYZ
 
Vec4FromPoint und Vec4FromDirection sind nicht dasselbe. Ein Punkt trägt w = 1 und wird von einer Verschiebung mitgenommen; eine Richtung trägt w = 0 und bleibt davon unberührt. Wer eine Normale als Punkt transformiert, verschiebt sie mit — und bekommt eine Beleuchtung, die sich mit der Kamera bewegt.

Dafür gibt es auch die fertigen Wege Mat4MulPoint3 und Mat4MulDir3.


3. Mat3

Bereich Funktionen
Bauen Mat3New, Mat3Zero, Mat3Identity, Mat3FromRows, Mat3FromCols
Zugriff Mat3Row(m, i), Mat3Col(m, j)
Rechnen Mat3Add, Mat3Sub, Mat3Scale, Mat3Mul, Mat3MulVec3, Mat3Transpose, Mat3Trace, Mat3Det
Invertieren Mat3Inverse(m): Mat3Inv
2D-Transformationen Mat3Translate2D, Mat3Scale2D, Mat3Rotate2D

Die 2D-Funktionen nutzen Mat3 als homogene 3×3-Matrix — der übliche Weg, eine Verschiebung in der Ebene als Matrix zu führen.


4. Mat4

Bereich Funktionen
Bauen Mat4Identity, Mat4Zero, Mat4FromRows, Mat4FromCols, Mat3ToMat4, Mat4ToMat3
Rechnen Mat4Add, Mat4Sub, Mat4ScaleBy, Mat4Mul, Mat4Transpose, Mat4Trace, Mat4Det
Anwenden Mat4MulVec4, Mat4MulPoint3, Mat4MulDir3, Mat4ProjectPoint3
Invertieren Mat4Inverse(m): Mat4Inv
Transformationen Mat4Translate, Mat4ScaleXYZ, Mat4RotateX/Y/Z, Mat4RotateAxis
Kamera und Projektion Mat4LookAt, Mat4Perspective, Mat4Ortho, Mat4Frustum

5. Ergebnisse, die scheitern können

pub type Mat4Inv    = struct { ok: bool; m: Mat4; }
pub type Mat3Inv    = struct { ok: bool; m: Mat3; }
pub type Vec3Result = struct { ok: bool; v: Vec3; }

Eine singuläre Matrix hat keine Inverse, und eine perspektivische Teilung durch w = 0 kein Ergebnis. Beides liefert deshalb ein Struct mit ok-Feld statt einer Zahl, die nach Ergebnis aussieht:

var inv: Mat4Inv := Mat4Inverse(m);
if (inv.ok) { /* inv.m benutzen */ }

Betroffen sind Mat3Inverse, Mat4Inverse, Vec4PerspectiveDivide und Mat4ProjectPoint3.

MatEpsilon() liefert die Schranke, ab der eine Determinante als null gilt — dieselbe, die …Equals(a, b, eps) als Vorgabe nimmt.


6. Beispiel

unit main;
import std.matrix;
import std.io;
import std.string;

fn main(): int64 {
    // erst drehen, dann verschieben
    var m: Mat4 := Mat4Mul(Mat4Translate(Vec3New(1.0, 2.0, 3.0)),
                           Mat4RotateZ(1.5707963268));

    var p: Vec3 := Mat4MulPoint3(m, Vec3New(1.0, 0.0, 0.0));
    PrintLn(StrConcat("Punkt: ", StrConcat(FloatToStr(p.x, 3),
             StrConcat(" ", StrConcat(FloatToStr(p.y, 3),
             StrConcat(" ", FloatToStr(p.z, 3)))))));

    var inv: Mat4Inv := Mat4Inverse(m);
    PrintLn(StrConcat("invertierbar: ", BoolToStr(inv.ok)));
    return 0;
}

Punkt: 1.000 3.000 3.000
invertierbar: true


7. Warum der Quelltext umständlich aussieht

Die Unit trägt sechzehn benannte f64-Felder statt eines Arrays und schreibt 0.0 - x statt -x. Das war kein Stil, sondern Notwehr gegen drei Compilerdefekte:

Issue Was nicht ging Stand
#1496 negatives Literal als Operand (x * -2.0) behoben, nachgemessen mit 1.2.2D
#1499 f64-con als Operand (x * PI) behoben
#1498 Strukturfeld [N]f64 rechnete ganzzahlig behoben

Alle drei sind inzwischen erledigt — die Umgehungen im Quelltext sind damit historisch, nicht mehr nötig. Für Anwender ändert sich nichts: die API war davon nie betroffen.


8. Abgrenzung

Aufgabe Unit
Feste 3×3/4×4-Matrizen, Grafik-Transformationen std.matrix
Vektoren ohne Matrizen std.coordinates.vec3
Drehungen ohne Gimbal Lock, Interpolation std.quat
Beliebig große Matrizen, Gleichungssysteme std.linalg

Letzte Aktualisierung: 2026-09-06 — Seite neu angelegt; erhoben aus std/matrix.lyx, Beispiel gegen lyxc 1.2.2D übersetzt und ausgeführt.