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
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.
