von Matrix Klasse erben
-
Hallo,
Ich habe eine Matrix Klasse, die ich als Basisklasse haben wollte.#pragma once #include "Array2D_T.h" namespace zp { template<typename T> class Matrix2D { protected: zp::Array2D<T> Matrix; public: inline Matrix2D() = default; inline virtual ~Matrix2D() = default; inline Matrix2D(const int rows, const int columns, const T v = 0) : Matrix(rows, columns, v) {} inline const zp::Array2D<T>& matrix() const { return Matrix; } inline zp::Array2D<T>& matrix() { return Matrix; } inline const Matrix2D& operator * (const Matrix2D& mT) const { try { if (Matrix.columns() != mT.Matrix.rows()) throw std::exception("zp::Matrix2D::operator * (Matrix2D)"); } catch (const std::exception& err) { std::cerr << "\nerror: " << err.what() << std::endl; return *this; } static Matrix2D nmT(Matrix.rows(), mT.Matrix.columns()); T v = 0; for (auto c = 0; c < mT.Matrix.columns(); ++c) { for (auto r = 0; r < Matrix.rows(); ++r) { v = this->Matrix(r, 0) * mT.Matrix(0, c); for (auto l = 1; l < Matrix.columns(); ++l) { v += this->Matrix(r, l) * mT.Matrix(l, c); } nmT.Matrix(r, c) = v; } v = 0; } return nmT; } inline const Matrix2D& operator + (const T v) const { static Matrix2D nmT(Matrix.rows(), Matrix.columns()); for (auto r = 0; r < Matrix.rows(); ++r) for (auto c = 0; c < Matrix.columns(); ++c) nmT.Matrix(r, c) = this->Matrix(r, c) + v; return nmT; } // // weitere Rechenoperationen... // }; }
Nun habe ich eine Klasse zB
Mat3x3<T>
die davon erben soll#pragma once #include "Matrix2D_T.h" namespace zp { template<typename T> class Mat3x3 : public Matrix2D<T> { public: inline Mat3x3() : Matrix2D(3, 3) { makeIdentity(); } private: inline void makeIdentity() { this->Matrix(0, 0) = 1; this->Matrix(1, 1) = 1; this->Matrix(2, 2) = 1; } }; }
Da bekomme ich folgende Fehlermeldung bei den Rechenoperationen:
Error (active) E0312 no suitable user-defined conversion from "const zp::Matrix2D<float>" to "zp::Mat3x3<float>" exists
.Jetzt habe ich die Klasse
Mat3x3<T>
als eigenständige Klasse geschrieben#pragma once #include "Matrix2D_T.h" namespace zp { template<typename T> class Mat3x3 { Matrix2D<T> Matrix; public: inline Mat3x3() : Matrix(3, 3) { makeIdentity(); } inline const Matrix2D<T>& mat() const { return Matrix; } inline Matrix2D<T>& mat() { return Matrix; } private: inline void makeIdentity() { Matrix.matrix()(0, 0) = 1; Matrix.matrix()(1, 1) = 1; Matrix.matrix()(2, 2) = 1; } }; }
Geht dann, aber die Aufrufe sehen ziemlich wild aus
mat3x3.mat().matrix()
.Also wieso klappt die Vererbung bei mir nicht? Oder was wäre da besser?
-
Wie rufst du denn die Operatoren auf? Bedenke, daß du bisher
const Matrix2D&
zurückgibst.
Du solltest auch eine Kopie zurückgeben und nicht die Referenz auf eine statische Instanz!Wenn du jedoch den Datentyp der abgeleiteten Klasse zurückgeben möchtest, dann mußt du diese selbst als Template-Parameter der Basisklasse mitgeben: Curiously recurring template pattern (CRTP) (wie es z.B. auch Boost operators macht)
PS: Das
inline
bei den Memberfunktionen ist überflüssig - das ist automatisch so.
-
@Th69 sagte in von Matrix Klasse erben:
Wenn du jedoch den Datentyp der abgeleiteten Klasse zurückgeben möchtest,
Kann ich wegen dem "jedoch" die Antwort so deuten, das es zwei Möglichkeiten gibt? Kopie zurückgeben oder CRTP? Und wie meinst Du "Operatoren aufrufen"?. Bezieht sich das auf dies zB:
zp::Mat3x3<float> mat1; zp::Mat3x3<float> mat2; zp::Mat3x3<float> result = mat1 * mat2;
? Wobei dann in Zeile 3 der Fehler auftritt.
PS: Das
inline
bei den Memberfunktionen ist überflüssig - das ist automatisch so.Ok, ich dachte bei Template Funktionen sollte man das trotzdem setzen. Aber ist ja kein Ding.
-
Genau diesen Code meinte ich:
zp::Mat3x3<float> result = mat1 * mat2;
Der
*
-Operator erzeugt ein BasisMatrix2D<float>
Objekt (bzw. in deinem Code eine konstante Referenz darauf), du willst es aber der abgeleiteten KlasseMat3x3<float>
zuweisen und das geht nun mal nicht direkt (daher die entsprechende Fehlermeldung und mein Hinweis auf CRTP).Benötigst du denn überhaupt die Polymorphie (willst du also verschiedene Klassen von
Matrix2D<T>
ableiten und dann verschiedene Objekte davon gemeinsam verwalten)?
Ansonsten solltest du einfachRows
undColumns
als weitere Template-Parameter derMatrix2D<T>
Klasse mitgeben.
Und zur einfachen Benutzung dann entsprechende Type-Aliase anlegen:typedef Matrix2D<float, 3, 3> Mat3x3; // bzw. using Mat3x3 = Matrix2D<float, 3, 3>;
-
eventuell wäre es auch nicht schlecht wenn du dir mal anschaust wie es andere machen.
z.b. glm (https://github.com/g-truc/glm)
-
Genau wie ich schrieb, es wird jeweils ein
typedef
erzeugt, z.B. glm/ext/matrix_float3x3.hpp.
-
@Th69 sagte in von Matrix Klasse erben:
Ansonsten solltest du einfach
Rows
undColumns
als weitere Template-Parameter derMatrix2D<T>
Klasse mitgeben.
Und zur einfachen Benutzung dann entsprechende Type-Aliase anlegen:typedef Matrix2D<float, 3, 3> Mat3x3; // bzw. using Mat3x3 = Matrix2D<float, 3, 3>;
template <typename T> using Mat3x3 = zp::Matrix2D<T>(3, 3);
Hm, das bekomme ich
to many arguments for class template zp::Matrix2D
.
Aber ich hatte das ähnlich, und zwartemplate <typename T> using Mat3x3 = zp::Matrix2D<T>(3, 3);
Muss ich jetzt mal überprüfen, warum ich das nicht weiter verfolgt habe. Danke erstmal.
Ich muss mir dann noch überlegen, wie ich die MethodemakeIdentity()
unterbringe ohne sie jedesmal händisch aufzurufen.Im Konstruktor von Matrix2D ist sie ja unpassend.
-
Ich schrieb Template-Parameter, also
template<typename T, size_t Rows, size_t Columns> class Matrix2D
(evtl. noch mit Default-Werten, also z.B.
size_t Columns = 1
)Die Methode
makeIdentity()
kann ja mit einer Schleife allgemein geschrieben werden (wobei beiRows != Columns
es eigentlich keine Einheitsmatrix gibt).
-
@Th69 sagte in von Matrix Klasse erben:
Ich schrieb Template-Parameter, also
template<typename T, size_t Rows, size_t Columns> class Matrix2D
Ah ok, da habe ich wieder nicht lange genug nachgedacht, sondern sofort geantwortet. Das werde ich jetzt nicht so machen, sondern über Deinen zweiten Hinweis erst mal nachdenken. Brauche immer etwas länger.
-
@Th69 sagte in von Matrix Klasse erben:
(wobei bei
Rows != Columns
es eigentlich keine Einheitsmatrix gibt).Genau, aber bei der Multiplikation muss beachtet werden
Die Matrizenmultiplikation oder Matrixmultiplikation ist in der Mathematik eine multiplikative
Verknüpfung von Matrizen. Um zwei Matrizen miteinander multiplizieren zu können, muss die
Spaltenzahl der ersten Matrix mit der Zeilenzahl der zweiten Matrix übereinstimmen.Deshalb will ich diese Methode auch nur bei quadratischen Matrizen haben und nicht im Konstruktor von Matrix2D.
Ich weiß, das ist Dir klar, aber ich habe geantwortet.
-
Wäre es nicht besser eine statische Klassenmethode zu schreiben, die einem eine Identity Matrix gibt? Also quasi eine Factory Methode.
Siehe z.B. https://numpy.org/doc/stable/reference/generated/numpy.identity.html
Finde ich einfach praktischer als wenn der Konstruktor das macht. Da weiß man wenigstens direkt was man bekommt. Und dadurch das du nur eine Dimension angibst, hast du dann auch automatisch deine quadratische Matrix
-
@zeropage sagte in von Matrix Klasse erben:
Deshalb will ich diese Methode auch nur bei quadratischen Matrizen haben und nicht im Konstruktor von Matrix2D.
Um nochmal auf dein ursprüngliches Problem zu kommen: Dir ist bewusst, dass
Mat3x3
Konvertierungs- und Zusweisungsoperatoren fürMatrix2D
-Typen braucht, damit der Fehler nicht auftritt? Die werden nämlich nicht automatisch erzeugt, sondern- nur die für denMat3x3
-Typ selbst. Das funktioniert nicht, da eineMatrix2D
nicht mit demMat3x3(const Mat3x3&)
Default-Kopierkonstruktor bzw. deroperator=(const Mat3x3&)
Default-Zuweisung aufgerufen werden kann.Wenn du die Dimensionen in Template-Parameter packen willst, stellt sich die Frage, ob das eine dynamische Matrix-Klasse werden soll, bei der die Dimensionen zur Laufzeit festgelegt werden, oder ob sie statisch sein soll - d.h. Zeilen- und Spaltenzahl sind zur Compile-Zeit bekannt. Für eine dynamische Matrix-Klasse ist dein ursprünglicher Ansatz gar nicht so verkehrt, du brauchts halt die Konvertierungs-Operatoren, damit das funktioniert.
Wenn das eine statische Matrix werden soll, also Zeilen- und Spaltenzahl als Template-Parameter, dann kann man die zusätzlichen Funktionen wie
identity()
z.B. zur Compilezeit "hinzuschalten".enable_if
ist ein Ansatz, ich hab das allerdings mal mit Mixin-Basisklassen gelöst, was ungefähr so aussah (grober Prototyp mit etwas redundantem Code):#include <iostream> template <typename T, int M, int N> class Matrix; template <typename T, int M, int N, bool IS_VECTOR = (M == 1 || N == 1)> struct MatrixAccessorsBase { auto& operator()(int i, int j) const { return static_cast<const Matrix<T, M, N>*>(this)->elements[i * N + j]; } auto& operator()(int i, int j) { return static_cast<Matrix<T, M, N>*>(this)->elements[i * N + j]; } }; template <typename T, int M, int N> struct MatrixAccessorsBase<T, M, N, true> { auto& operator()(int i) const { return static_cast<const Matrix<T, M, N>*>(this)->elements[i]; } auto& operator()(int i) { return static_cast<Matrix<T, M, N>*>(this)->elements[i]; } auto& operator()(int i, int j) const { return static_cast<const Matrix<T, M, N>*>(this)->elements[i * N + j]; } auto& operator()(int i, int j) { return static_cast<Matrix<T, M, N>*>(this)->elements[i * N + j]; } }; template <typename T, int M, int N, bool IS_QUADRATIC = (M == N)> struct QuadraticMatrixBase { }; template <typename T, int M, int N> struct QuadraticMatrixBase<T, M, N, true> { static const auto& identity() { static auto m = []{ Matrix<T, M, N> m; for (int i = 0; i < M; ++i) m(i, i) = 1; return m; }(); return m; } }; template <typename T, int M, int N> struct Matrix : MatrixAccessorsBase<T, M, N>, QuadraticMatrixBase<T, M, N> { T elements[M * N] = {}; }; template <typename T, int M, int N> auto operator<<(std::ostream& out, const Matrix<T, M, N>& m) -> std::ostream& { if (M > 1 || N > 1) out << "{ "; for (int i = 0; i < M; ++i) { if (i > 0) out << ", "; if (N > 1) out << "{ "; out << m(i, 0); for (int j = 1; j < N; ++j) out << ", " << m(i, j); if (N > 1) out << " }"; } if (M > 1 || N > 1) out << " }"; return out; } template <typename T, int N> using Vector = Matrix<T, N, 1>; template <typename T, int N> using QuadraticMatrix = Matrix<T, N, N>; auto main() -> int { Matrix<float, 2, 3> matrix; Vector<float, 3> vector; matrix(0, 2) = 42; matrix(1, 2) = 13; vector(2) = 7; std::cout << matrix << std::endl; std::cout << vector << std::endl; std::cout << QuadraticMatrix<float, 3>::identity() << std::endl; }
https://godbolt.org/z/Ec8z3bEj5
Vielleicht ist das ja auch für dich ein brauchbarer Ansatz
-
@Leon0402
also solch eine Methode:static Matrix2D Identity(const int size) { Matrix2D unit(size, size); for (auto i = 0; i < size; ++i) unit.matrix()(i, i) = 1; return unit; }
@Finnegan
Ok danke, so wie ich das sehe, ist DeineMatrixAccessorsBase
meinArray2D<T>
. Die Zugriffsoperatoren habe ich aslo schon, ich brauch nur noch die Rechenoperationen, die ich inMatrix2D<T>
habe.Und ich hätte schon gerne jede spezielle Matrix als eigenen Typ, wo ich aber nicht jedesmal alle Rechenoperationen implementieren muss. Deshalb kam ich auf Vererbung.
Also schlußendlich möchte ich mit den Matrizen so umgehen, wie in diesen Funktionen:
template<typename T> inline static zp::Mat3x3<T> makeTranslationMatrix(const zp::Vector2D<T>& translate) { zp::Mat3x3<T> mat; mat.mat().matrix()(2, 0) = translate.x; // ist hier noch meinen Versuchen geschuldet mat.mat().matrix()(2, 1) = translate.y; // sollte so aussehen: mat.matrix()(2, 1) = translate.y; return mat; }
oder
inline void transform(VectorObject2D<T>& shape) { const Mat3x3<T> transformMat = calculateMatrix(); std::vector<Vector2D<T>> points; for (const auto& point : shape.vertices()) points.push_back(point * transformMat); shape.getAttributesFrom(points); }
So hatte ich das eigentlich vor. Wahrscheinlich steckt die Lösung schon in Euren Antworten. Ich muss noch ein wenig forschen.
-
@zeropage sagte in von Matrix Klasse erben:
@Finnegan
Ok danke, so wie ich das sehe, ist DeineMatrixAccessorsBase
meinArray2D<T>
.Nein. Für
MatrixAccessorsBase
gibt es bei dir keine Entsprechung. Das ist eine Klasse, die keinerlei Daten-Member hat und lediglich Funktionalität zur Verfügung stellt. In anderen Sprachen nennt man sowas schonmal Mixin, auch wenn man den Begriff in C++ eher selten verwendet.Die Funktionalität von
MatrixAccessorsBase
ist "Element zurückgeben", die eigentlichen Daten sind jedoch inMatrix::elements
gespeichert, also in einer Klasse, die vonMatrixAccessorsBase
erbt. Der Zugriff auf die Daten erfolgt de facto wie mit CRTP, auch wenn das in dem Code hier nicht direkt ersichtlich ist:z.B. Zeile 35:
return static_cast<const Matrix<T, M, N>*>(this)->elements[i * N + j];
MatrixAccessorsBase
castet hier seinen eigenenthis
-Pointer in einenMatrix<T, M, N>
-Pointer - das ist ein Downcast wie bei CRTP. Über diesen Pointer ist dann derelements
-Datenmember der vonMatrixAccessorsBase
abgeleitetenMatrix
verfügbar.So können Member-Funktionen von
MatrixAccessorsBase
auf Daten der abgeleiteten Klasse arbeiten, also lediglich "Funktionen zur Verfügung stellen", ohne eigene Daten-Member zu haben. Die Idee dahinter ist, dass diese Funktionalität bei Bedarf "zugeschaltet" wird, je nachdem welche Eigenschaften dieMatrix
-Klasse hat. Im Fall vonMatrixAccessorsBase
ist sind das die()
-Operatoren.Der einzige Grund, die in diese Mixin-Klasse zu packen ist hier für einen Vektor, also eine Matrix, bei der entweder Zeilen oder Spalten gleich 1 sind, auch noch einen
operator(int i)
zu haben, mit dem man mit nur einen Index-Parameter auf die Elemente zugreifen kann. Siehe Zeile 107:vector(2) = 7;
Sorry wenn
MatrixAccessorsBase
verwirrend gewesen sein sollte, das diente eigentlich nur zur Illustration.QuadraticMatrixBase
wird ähnlich verwendet, auch wenn hier kein CRTP-Downcast stattfindet, da die Klasse lediglich eine statische Member-Funktion zur Verfügung stellt. Diese Basisklasse ist per default leer, falls aberM == N
gilt, es sich also um eine quadratische Matrix handelt, dann hat sie eine statischeidentity()
-Memberfunktion, welche die Einheitsmatrix zurückgibt.Die Zugriffsoperatoren habe ich aslo schon, ich brauch nur noch die Rechenoperationen, die ich in
Matrix2D<T>
habe.Wie gesagt, meine Zugriffsoperatoren dienten nur zur Illustration, wie man diese "Mixin-Klassen" einsetzt und von diesen aus auf Elemente der Matrix zugreift.
Und ich hätte schon gerne jede spezielle Matrix als eigenen Typ, wo ich aber nicht jedesmal alle Rechenoperationen implementieren muss. Deshalb kam ich auf Vererbung.
Wenn sich die Template-Argumente unterscheiden, dann sind das schon jeweils verschiedene Typen. Ansonsten sollte eigetlich auch dein ursprünglicher Ansatz funktionieren, wenn dir das lieber ist. Du brauchst halt lediglich die konvertierenden Konstruktoren und Zuweisungsoperatoren. Die Rechenoperationen kannst du schon von
Matrix2D
wiederverwenden. Der weiss halt nur nicht, wie er eineMatrix2D
in eineMat3x3
konvertieren soll.Und auch mit meinem Ansatz braucht man die Rechenoperationen nur einmal zu implementieren. Man kann sogar einiges zwischen "Vektor" und "Matrix" wiederverwenden. So eine Vektoraddition bekäme man z.B. geschenkt, wenn man eine Matrixaddition implementiert hat und ein Vektor lediglich eine
Matrix<T, N, 1>
oderMatrix<T, 1, N>
ist. Oder mein Steam-operator<<
- einmal implementiert und gibt Vektoren sowie Matrizen aus.
-
@zeropage sagte in von Matrix Klasse erben:
inline static zp::Mat3x3<T> makeTranslationMatrix
Noch ein Gedanke am Rande: Willst du damit nur so geometrisches Zeug machen und brauchst eventuell gar keine Matrizen, die nicht-quadratisch sind? Das wäre auch noch ein Ansatz: Es gibt halt nur quadratische Matrizen, ergo nur eine Matrix-Klasse
-
Erstmal wow!, wegen Deiner ausfürlichen Erläuterung. Vielen Dank, ich bin mir nicht sicher, ob ich überhaupt soviel Text verdiene
Ja, zur Zeit benötige ich nur quadratische Matrizen, eigentlich nur Mat3x3 und Mat4x4. Allerdings will ich in meinem Projekt auf alles vorbereitet sein. Ich programmiere mangels Kreativität gerne Sachen auf YouTube nach und da möchte ich mir einen möglichst großen Grundstock anlegen.
Im Moment aber hört sich Dein Vorschlag ganz vernünftig an. Ich bin etwas hin- und hergerissen was ich nun mache, auch was die Lösung in diesem Fall angeht.
-
@zeropage sagte in von Matrix Klasse erben:
Im Moment aber hört sich Dein Vorschlag ganz vernünftig an. Ich bin etwas hin- und hergerissen was ich nun mache, auch was die Lösung in diesem Fall angeht.
Um dich noch mehr hin- und her zu reissen: Mein Vorschlag ist auch nur ein Ansatz unter vielen und der funktioniert auch so nur mit statischen Matrizen - bei dynamischen weiss man ja nicht zur Compile-Zeit ob sie quadratisch sind oder nicht.
Der grosse Vorteil von den Mixins ist allerdings, dass letztendlich alles dasselbe
Matrix<T, M, N>
-Template ist und man Operationen wie Multiplikation nicht für eine abgeleiteteQuadraticMatrix
duplizieren muss oder irgendwelche Konvertierungs-Operatoren braucht. Ein einzigerMatrix<...> operator*(const Matrix<...>&, const Matrix<...>&)
reicht und greift für beide.Den selben Effekt kann man übrigens auch mit
std::enable_if
erzielen, die CRTP-Mixins find ich aber eleganter und leichter zu lesen... weniger "Template-Rauschen" im QuellcodeUnd am Rande, damit keine Missverständnisse aufkommen: Die
Base
-Mixin-Klassen werden nur für Funktionen benötigt, die nicht von allen Matritzen unterstützt werden. Alles andere kann man direkt inMatrix
implementieren.
-
@zeropage sagte in von Matrix Klasse erben:
Also schlußendlich möchte ich mit den Matrizen so umgehen, wie in diesen Funktionen:
template<typename T> inline static zp::Mat3x3<T> makeTranslationMatrix(const zp::Vector2D<T>& translate) { zp::Mat3x3<T> mat; mat.mat().matrix()(2, 0) = translate.x; // ist hier noch meinen Versuchen geschuldet mat.mat().matrix()(2, 1) = translate.y; // sollte so aussehen: mat.matrix()(2, 1) = translate.y; return mat; }
Irgendwie ist mit das ganz schön viel "
mat
" hier:mat.mat().matrix()
und auchmat.matrix()(2, 1)
ist jetzt so viel besser ... sollMat3x3
keine Operatoren für Elementzugriff bekommen oder warum so umständlich? Ginge nicht auch einfachmat(2, 0) = translate.x
?Meine Wunschsyntax sähe hier übrigens eher so aus:
auto mat = Mat3x3::identity(); submatrix<2, 1>(mat, 0, 2) = translate; return mat;
submatrix<M, N>(m, i, j)
würde einenMatrix<T, M, N>
-View auf eine Untermatrix vonm
zurückliefern, der bei Indizesi
,j
beginnt. Quasi einstd::span
für zweidimensionale Datenbereiche. Z.B. Vektoren in Spalten/Zeilen einer Matrix zu kopieren oder aus diesen auszulesen ist etwas, das man bei geometrischen Problemen tatsächlich öfter mal braucht. Der Translationsvektor, der in die letzte Spalte einer Einheitsmatrix kopiert wird, um eine Translationsmatrix zu erhalten, ist da ein gutes Beispiel für.Das bräuchte allerdings noch etwas Arbeit, damit das so funktionert. Ist vielleicht noch etwas Overkill im Moment. Machbar wäre es jedenfalls
P.S.: Falls es zu Verwirrungen bezüglich Indizes kommen sollte, ich verwende da die mathematische Index-Reihenfolge.
M
Zeilen xN
Spalten miti
als Zeilen- undj
als Spaltenindex. Bei deiner Translationsmatrix sieht es so aus als ob du das anders herum machst. Ist nicht falsch, muss man nur wissen.
-
Eigentlich mache ich auch
(rows, columns)
. Warum das bei meiner Matrix einen anderen Eindruck macht, kann ich jetzt gar nicht erklären. Sieht eine Translationsmatrix in 2D nicht so aus?| 1 0 0 | | 0 1 0 | | X Y 1 |
@Finnegan sagte in von Matrix Klasse erben:
Ginge nicht auch einfach
mat(2, 0) = translate.x
?Wäre schöner, ja. Aber ich habe doch als Matrix in
Matrix2D
den TypArray2D
. Und um darauf zuzugreifen habe ichmatrix()
als Rückgabemethode.
-
Das hier wäre wohl quick & simple
?
struct mat4x4 { float matrix[4][4] = { 0 }; mat4x4() { matrix[0][0] = 1.0f; matrix[1][1] = 1.0f; matrix[2][2] = 1.0f; matrix[3][3] = 1.0f; } }; inline Vec3D multiplyVector(const mat4x4& m, const Vec3D& i) { Vec3D v; v.x = i.x * m.matrix[0][0] + i.y * m.matrix[1][0] + i.z * m.matrix[2][0] + i.w * m.matrix[3][0]; v.y = i.x * m.matrix[0][1] + i.y * m.matrix[1][1] + i.z * m.matrix[2][1] + i.w * m.matrix[3][1]; v.z = i.x * m.matrix[0][2] + i.y * m.matrix[1][2] + i.z * m.matrix[2][2] + i.w * m.matrix[3][2]; v.w = i.x * m.matrix[0][3] + i.y * m.matrix[1][3] + i.z * m.matrix[2][3] + i.w * m.matrix[3][3]; return v; } inline mat4x4 multiplyMatrix(const mat4x4& lhs, const mat4x4& rhs) { mat4x4 outMat; for (std::size_t c = 0; c < 4; c++) for (std::size_t r = 0; r < 4; r++) outMat.matrix[r][c] = lhs.matrix[r][0] * rhs.matrix[0][c] + lhs.matrix[r][1] * rhs.matrix[1][c] + lhs.matrix[r][2] * rhs.matrix[2][c] + lhs.matrix[r][3] * rhs.matrix[3][c]; return outMat; } inline mat4x4 makeRotationX(const float fAngleRad) { mat4x4 outMat; outMat.matrix[1][1] = std::cosf(fAngleRad); outMat.matrix[1][2] = -std::sinf(fAngleRad); outMat.matrix[2][1] = std::sinf(fAngleRad); //outMat.matrix[1][2] = std::sinf(fAngleRad); //outMat.matrix[2][1] = -std::sinf(fAngleRad); outMat.matrix[2][2] = std::cosf(fAngleRad); return outMat; } inline mat4x4 makeRotationY(const float fAngleRad) { mat4x4 outMat; outMat.matrix[0][0] = std::cosf(fAngleRad); outMat.matrix[0][2] = std::sinf(fAngleRad); outMat.matrix[2][0] = -std::sinf(fAngleRad); outMat.matrix[2][2] = std::cosf(fAngleRad); return outMat; } inline mat4x4 makeRotationZ(const float fAngleRad) { mat4x4 outMat; outMat.matrix[0][0] = std::cosf(fAngleRad); outMat.matrix[0][1] = std::sinf(fAngleRad); outMat.matrix[1][0] = -std::sinf(fAngleRad); outMat.matrix[1][1] = std::cosf(fAngleRad); //outMat.matrix[2][2] = 1.0f; //outMat.matrix[3][3] = 1.0f; return outMat; } inline mat4x4 makeTranslation(const float x, const float y, const float z) { mat4x4 outMat; //outMat.matrix[0][0] = 1.0f; //outMat.matrix[1][1] = 1.0f; //outMat.matrix[2][2] = 1.0f; //outMat.matrix[3][3] = 1.0f; outMat.matrix[3][0] = x; outMat.matrix[3][1] = y; outMat.matrix[3][2] = z; return outMat; } inline mat4x4 makeScalation(const float x, const float y, const float z) { mat4x4 outMat; outMat.matrix[0][0] = x; outMat.matrix[1][1] = y; outMat.matrix[2][2] = z; //outMat.matrix[3][3] = 1.0f; return outMat; } inline mat4x4 makeReflectionX() { mat4x4 outMat; outMat.matrix[1][1] = 1.0f; outMat.matrix[1][1] = -1.0f; outMat.matrix[2][2] = 1.0f; return outMat; } inline mat4x4 makeReflectionY() { mat4x4 outMat; outMat.matrix[1][1] = -1.0f; outMat.matrix[1][1] = 1.0f; outMat.matrix[2][2] = 1.0f; return outMat; } inline mat4x4 makeProjection(const float fFovDegrees, const float fAspectRatio, const float fNear, const float fFar) { float fFovRad = 1.0f / std::tanf(math::toRadian<float>(fFovDegrees / 2.f)); mat4x4 outMat; outMat.matrix[0][0] = fAspectRatio * fFovRad; outMat.matrix[1][1] = fFovRad; outMat.matrix[2][2] = fFar / (fFar - fNear); outMat.matrix[3][2] = (-fFar * fNear) / (fFar - fNear); outMat.matrix[2][3] = 1.0f; outMat.matrix[3][3] = 0.0f; return outMat; } inline mat4x4 quickInverse(const mat4x4& mat) // Only for Rotation/Translation Matrices { mat4x4 outMat; outMat.matrix[0][0] = mat.matrix[0][0]; outMat.matrix[0][1] = mat.matrix[1][0]; outMat.matrix[0][2] = mat.matrix[2][0]; outMat.matrix[0][3] = 0.0f; outMat.matrix[1][0] = mat.matrix[0][1]; outMat.matrix[1][1] = mat.matrix[1][1]; outMat.matrix[1][2] = mat.matrix[2][1]; outMat.matrix[1][3] = 0.0f; outMat.matrix[2][0] = mat.matrix[0][2]; outMat.matrix[2][1] = mat.matrix[1][2]; outMat.matrix[2][2] = mat.matrix[2][2]; outMat.matrix[2][3] = 0.0f; outMat.matrix[3][0] = -(mat.matrix[3][0] * outMat.matrix[0][0] + mat.matrix[3][1] * outMat.matrix[1][0] + mat.matrix[3][2] * outMat.matrix[2][0]); outMat.matrix[3][1] = -(mat.matrix[3][0] * outMat.matrix[0][1] + mat.matrix[3][1] * outMat.matrix[1][1] + mat.matrix[3][2] * outMat.matrix[2][1]); outMat.matrix[3][2] = -(mat.matrix[3][0] * outMat.matrix[0][2] + mat.matrix[3][1] * outMat.matrix[1][2] + mat.matrix[3][2] * outMat.matrix[2][2]); outMat.matrix[3][3] = 1.0f; return outMat; }
-
@zeropage sagte in von Matrix Klasse erben:
Wäre schöner, ja. Aber ich habe doch als Matrix in Matrix2D den Typ Array2D. Und um darauf zuzugreifen habe ich matrix() als Rückgabemethode.
https://martinfowler.com/bliki/TellDontAsk.html
Du willst doch eig. nich unbedingt, dass man auf den darunterliegenden Datentypen zugreifen muss, oder?