Wie soll ich Matrizen in C ++ darstellen?

7

Ich unterrichte mich selbst in 3D-Mathematik und baue dabei meine eigene rudimentäre Engine (sozusagen). Ich habe mich gefragt, wie ich meine C ++ - Matrixklasse am besten strukturieren kann. Es gibt einige Optionen:

  1. Separate Mitgliedsvariablen:

    struct Mat4 {
        float
            m11, m12, m13, m14,
            m21, m22, m23, m24,
            m31, m32, m33, m34,
            m41, m42, m43, m44;
    
        // methods
    
    }
  2. Ein mehrdimensionales Array:

    struct Mat4 {
        float[4][4] m;
    
        // methods
    
    }
  3. Ein Array von Vektoren

    struct Mat4 {
        Vec4[4] m;
    
        // methods
    
    }

Ich vermute, dass es für jeden positive und negative Aspekte geben würde.

Aus 3D Math Primer für Grafik und Spieleentwicklung, 2. Auflage S.155:

n-mal-m-Matrix.  Quelle (Wikipedia)

Matrizen verwenden 1-basierte Indizes, daher sind die erste Zeile und Spalte mit 1 nummeriert. Beispiel a12(lesen Sie „ aeins zwei“, nicht „ azwölf“) ist das Element in der ersten Zeile und zweiten Spalte. Beachten Sie, dass sich dies von Programmiersprachen wie C ++ und Java unterscheidet, die 0-basierte Array-Indizes verwenden. Eine Matrix hat keine Spalte 0 oder Zeile 0. Dieser Unterschied in der Indizierung kann zu Verwirrung führen, wenn Matrizen unter Verwendung eines tatsächlichen Array-Datentyps gespeichert werden. Aus diesem Grund ist es üblich, dass Klassen, die kleine Matrizen fester Größe des Typs speichern, der für geometrische Zwecke verwendet wird, jedem Element eine eigene benannte Elementvariable zuweisen, z. B. float a11anstatt die native Array-Unterstützung der Sprache mit etwas wie float zu verwenden elem[3][3].

Das ist also eine Stimme für Methode eins.

Ist das wirklich die akzeptierte Art, Dinge zu tun? Es scheint ziemlich unhandlich, wenn der einzige Vorteil darin besteht, an der herkömmlichen mathematischen Notation festzuhalten.

bjz
quelle
2
Denken Sie zuerst an die Schnittstelle Ihrer Matrix (die Funktionen der öffentlichen Mitglieder). Beispielsweise benötigen Sie möglicherweise GetRow oder GetForwardVector. Überlegen Sie, wie Sie sie am effizientesten implementieren können. Dann passen die internen Datenstrukturen auf natürliche Weise zusammen
Maik Semder,
2
In C ++ können Sie übrigens sowohl Arrays als auch Mitgliedsvariablen gleichzeitig verwenden, Unions :)
API-Beast
1
Ich würde mich definitiv für Methode eins entscheiden, da Sie für viele lineare Algebra einige spezifische Zellen in Ihrer Matrix benötigen. Die Arrays enthalten niemals nur die Arrays, die Sie benötigen. Daher müssen Sie für einfache Fragen wie den oben genannten Forward-Vektor immer mehrere Arrays indizieren. Dies ist auch, wie Microsoft es in ihrem XNA-Framework macht (aber natürlich ist ein Unternehmen, das es tut, nicht das beste Argument)
Roy T.
2
Ich würde mich definitiv an nullindizierte Arrays halten - ich mache viel wissenschaftliches Rechnen, und es ist viel weniger verwirrend, die Indizes beim Wechsel zwischen Mathematik und Code zu konvertieren, als in Ihrem Code inkonsistent zu sein. Wenn Sie sich für Option 2 oder 3 entscheiden, ist es einfacher, Ihre Routinen mit Matrizen beliebiger Größe arbeiten zu lassen, die Sie möglicherweise für etwas benötigen (zum Beispiel haben Sie wahrscheinlich eine Matrixmultiplikationsroutine, die Sie verwenden könnte für Matrixmatrix oder Matrixvektor verwendet werden).
James

Antworten:

12

Warum wählen? Sie können beides haben. (Ohne zusätzliche Komplexität der Logik und ohne zusätzlichen Speicherbedarf dank Gewerkschaften.)

struct Mat4
{
    union
    {
        struct
        {
          float m11, m12, m13, m14,
                m21, m22, m23, m24,
                m31, m32, m33, m34,
                m41, m42, m43, m44;
        };
        float[4][4] m;
    };
// methods
}
API-Beast
quelle
1
+1, normalerweise verwende ich float [4] [4] m4x4 und float [16] m16 sowie die einzelnen Elemente, aber dies ist etwas, das Sie an die Anforderungen Ihres eigenen Programms anpassen.
Maximus Minimus
1
Beachten Sie, dass Sie häufig compilerspezifische Erweiterungen benötigen, damit diese spezielle Funktion funktioniert, da ISO C ++ keine anonymen Strukturen zulässt (in der Praxis funktioniert dies jedoch auf den meisten wichtigen Compilern mit Erweiterungen).
Ich denke, Sie würden mit diesem Ansatz durchkommen, aber ... Ist es laut ISO c ++ "erlaubt", ihn als "m" zu lesen, als er als "m11, m22, ..." geschrieben wurde?
André
1
@Andre ist von C ++ nicht explizit erlaubt, aber jeder große Compiler unterstützt es als dokumentierte Erweiterung (es wird als Typ-Punning durch anonyme Gewerkschaften bezeichnet). Es ist auch ausdrücklich in C.
Sam Hocevar
1
Du meinst, ich kann meinen Kuchen haben und ihn auch essen?! Vielen Dank, Kumpel! (es funktioniert auch in D, hehe)
bjz