commit 1dd80d61bf20fa7b7dcde10b29a4f9f31f292bd1 Author: william Date: Thu Jan 18 10:53:15 2024 -0500 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c7a2759 --- /dev/null +++ b/.gitignore @@ -0,0 +1,169 @@ +# Created by https://www.toptal.com/developers/gitignore/api/clion,cmake,c++ +# Edit at https://www.toptal.com/developers/gitignore?templates=clion,cmake,c++ + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CLion ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### CLion Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +# End of https://www.toptal.com/developers/gitignore/api/clion,cmake,c++ + diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..71e3aea --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +GTI320-labos \ No newline at end of file diff --git a/.idea/Lab1.iml b/.idea/Lab1.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/Lab1.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..83ae35c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2eecc60 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.15) +project(GTI320-labos) + +#-------------------------------------------------- +# Add google test and setup build +#-------------------------------------------------- +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +# No need for GMock +set( BUILD_GMOCK OFF CACHE BOOL "" FORCE) +set( INSTALL_GTEST OFF CACHE BOOL "" FORCE) +set( GTEST_FORCE_SHARED_CRT ON CACHE BOOL "" FORCE) +set( GTEST_DISABLE_PTHREADS ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +add_subdirectory(labo01) + +# TODO add nanogui setup and labo 02 and labo 03 + +if( MSVC ) + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT labo01) +endif() + diff --git a/labo01/CMakeLists.txt b/labo01/CMakeLists.txt new file mode 100644 index 0000000..21ca42b --- /dev/null +++ b/labo01/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.15) + +project(labo01) + +# Setup language requirements +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Add .cpp and .h files +set(HEADERS DenseStorage.h MatrixBase.h Matrix.h Math3D.h Vector.h Operators.h) +set(SOURCE main.cpp) +add_executable(labo01 ${SOURCE} ${HEADERS}) + +# Add linking information for Google Test +target_link_libraries(labo01 gtest) + +# Set labo01 as the startup project for Visual Studio +if( MSVC ) + set_property(TARGET labo01 PROPERTY VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/labo01) +endif() + diff --git a/labo01/DenseStorage.h b/labo01/DenseStorage.h new file mode 100644 index 0000000..e0a1bfd --- /dev/null +++ b/labo01/DenseStorage.h @@ -0,0 +1,217 @@ +#pragma once + +/** + * @file DenseStorage.h + * + * @brief Stockage dense pour des données à taille fixe ou dynamique. + * + * Nom: + * Code permanent : + * Email : + * + */ + +#include +#include + +namespace gti320 +{ + enum SizeType { Dynamic = -1 }; + + /** + * Stockage à taille fixe. + * + * Le nombre de données à stocker est connu au moment de la compilation. + * Ce nombre est donné par le paramètre de patron : _Size + * + * Un tampon (tableau) de taille `_Size_` est alloué sur la pile d'exécution. + */ + template + class DenseStorage + { + private: + + // TODO déclarer une variable m_data et allouer la mémoire pour y stocker _Size éléments + _Scalar* m_data; // <-- Ceci n'est pas bon, à modifier + + public: + + /** + * Constructeur par défaut + */ + DenseStorage() { } + + /** + * Constructeur de copie + */ + DenseStorage(const DenseStorage& other) + { + memcpy(m_data, other.m_data, sizeof(m_data)); + } + + /** + * Constructeur avec taille spécifiée + * + * (doit être la même que la taille spécifiée dans le patron) + */ + explicit DenseStorage(int _size) { } + + /** + * Constructor avec taille (_size) et données initiales (_data). + */ + explicit DenseStorage(const _Scalar* _data, int _size) + { + assert(_size >= 0 && _size == _Size); + memcpy(m_data, _data, sizeof(_Scalar) * _size); + } + + /** + * Opérateur de copie + */ + DenseStorage& operator=(const DenseStorage& other) + { + if (this != &other) + { + assert(other.size() == _Size); + memcpy(m_data, other.m_data, sizeof(m_data)); + } + return *this; + } + + static int size() { return _Size; } + + /** + * Redimensionne le stockage pour qu'il contienne `size` élément. + */ + void resize(int size) + { + // Ne rien faire. Invalide pour les matrices à taille fixe. + } + + /** + * Mets tous les éléments à zéro. + */ + void setZero() + { + memset(m_data, 0, sizeof(_Scalar) * _Size); + } + + /** + * Accès au tampon de données (en lecteur seulement) + */ + const _Scalar* data() const + { + return m_data; + } + + /** + * Accès au tampon de données (pour lecture et écriture) + */ + _Scalar* data() + { + return m_data; + } + }; + + + + /** + * Stockage à taille dynamique. + * + * Le nombre de données à stocker est déterminé à l'exécution. + * Un tampon de la taille demandée doit être alloué sur le tas via + * l'opérateur `new []` et la mémoire doit être libérée avec `delete[]` + */ + template + class DenseStorage<_Scalar, Dynamic> + { + private: + _Scalar* m_data; + int m_size; + + public: + + /** + * Constructeur par défaut + */ + DenseStorage() : m_data(nullptr), m_size(0) {} + + /** + * Constructeur avec taille spécifiée + */ + explicit DenseStorage(int _size) : m_data(nullptr), m_size(_size) + { + // TODO allouer un tampon pour stocker _size éléments de type _Scalar. + + // TODO initialiser ce tampon à zéro. + } + + /** + * Constructeur de copie + */ + DenseStorage(const DenseStorage& other) + : m_data(nullptr) + , m_size(other.m_size) + { + // TODO allouer un tampon pour stocker _size éléments de type _Scalar. + + // TODO copier other.m_data dans m_data. + + } + + /** + * Opérateur de copie + */ + DenseStorage& operator=(const DenseStorage& other) + { + // TODO implémenter ! + return *this; + } + + /** + * Destructeur + */ + ~DenseStorage() + { + // TODO libérer la mémoire allouée + + } + + /** + * Retourne la taille du tampon + */ + inline int size() const { return m_size; } + + /** + * Redimensionne le tampon alloué pour le stockage. + * La mémoire qui n'est plus utilisée doit être libérée. + * + * Note :​ Toutes opérations de redimensionnement entraînent une réallocation de mémoire. + * Il n’est pas pertinent de copier les données car le résultat serait de toute façon incohérent. + */ + void resize(int _size) + { + // TODO redimensionner la mémoire allouée + + } + + /** + * Met tous les éléments à zéro. + */ + void setZero() + { + // TODO implémenter ! + } + + /** + * Accès au tampon de données (en lecteur seulement) + */ + const _Scalar* data() const { return m_data; } + + /** + * Accès au tampon de données (pour lecture et écriture) + */ + _Scalar* data() { return m_data; } + }; + +} diff --git a/labo01/Math3D.h b/labo01/Math3D.h new file mode 100644 index 0000000..8c8d706 --- /dev/null +++ b/labo01/Math3D.h @@ -0,0 +1,110 @@ +#pragma once + +/** + * @file Math3D.h + * + * @brief Fonctions pour l'intinialisation et la manipulation de matrices de + * rotation, des matrices de transformations en coordonnées homogènes et les + * vecteurs 3D. + * + * Nom: + * Code permanent : + * Email : + * + */ + +#include "Matrix.h" +#include "Vector.h" +#include "Operators.h" + +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES +#endif + +#include + + +namespace gti320 { + + // Deux types de vecteurs 3D considérés ici + typedef Vector Vector3d; + typedef Vector Vector3f; + + // Dans le cadre de ce projet, nous considérons seulement deux + // cas : + // + // - les rotations + // - les translations + // + // Deux types de matrices en coordonnées homogèes : + typedef Matrix Matrix4d; + typedef Matrix Matrix4f; + // + // Deux types de matrices pour les rotations + typedef Matrix Matrix3d; + typedef Matrix Matrix3f; + + /** + * Initialise et retourne la matrice identité + */ + template<> + inline void Matrix4d::setIdentity() + { + // TODO affecter la valeur 0.0 partout, sauf sur la diagonale principale où c'est 1.0. + // Note: ceci est une redéfinition d'une fonction membre! + } + + /** + * Calcul de la matrice inverse, SPÉCIALISÉ pour le cas d'une matrice de + * transformation en coordonnées homogènes. + * + * TODO (vous pouvez supposer qu'il s'agit d'une matrice de transformation + * en coordonnées homogènes) + */ + template<> + inline Matrix4d Matrix4d::inverse() const + { + // TODO : implémenter + return Matrix4d(); // Pas bon, à changer + } + + /** + * Calcul de la matrice inverse, SPÉCIALISÉ pour le cas d'une matrice de rotation. + * + * (vous pouvez supposer qu'il s'agit d'une matrice de rotation) + */ + template<> + inline Matrix3d Matrix3d::inverse() const + { + // TODO : implémenter + return Matrix3d(); + } + + + /** + * Multiplication d'une matrice 4x4 avec un vecteur 3D où la matrice + * représente une transformation en coordonnées homogène. + */ + template + Vector<_Scalar, 3> operator*(const Matrix<_Scalar, 4, 4, ColumnStorage>& A, const Vector<_Scalar, 3>& v) + { + // TODO : implémenter + return Vector<_Scalar, 3>(); // pas bon, à changer + } + + + /** + * Initialise et retourne la matrice de rotation définie par les angles + * d'Euler XYZ exprimés en radians. + * + * La matrice doit correspondre au produit : Rz*Ry*Rx. + */ + template + static Matrix<_Scalar, 3, 3> makeRotation(_Scalar x, _Scalar y, _Scalar z) + { + // TODO : implémenter + + return Matrix<_Scalar, 3, 3>(); // pas bon, à changer + } + +} diff --git a/labo01/Matrix.h b/labo01/Matrix.h new file mode 100644 index 0000000..b6a9548 --- /dev/null +++ b/labo01/Matrix.h @@ -0,0 +1,343 @@ +#pragma once + +/** + * @file Matrix.h + * + * @brief Implémentation de matrices simples. + * + * Nom: + * Code permanent : + * Email : + * + */ + +#include "MatrixBase.h" + +namespace gti320 +{ + enum StorageType + { + ColumnStorage = 0, + RowStorage = 1 + }; + + // Déclaration avancée + template class SubMatrix; + + /** + * Classe Matrix spécialisé pour le cas générique. (defaut par colonne) + * + * (le cas d'un stockage par ligne fait l'objet d'une spécialisation de patron, voir plus bas) + */ + template + class Matrix : public MatrixBase<_Scalar, _RowsAtCompile, _ColsAtCompile> { + public: + + /** + * Constructeur par défaut + */ + Matrix() : MatrixBase<_Scalar, _RowsAtCompile, _ColsAtCompile>() { } + + /** + * Constructeur de copie + */ + Matrix(const Matrix& other) : MatrixBase<_Scalar, _RowsAtCompile, _ColsAtCompile>(other) { } + + /** + * Constructeur avec spécification du nombre de ligne et de colonnes + */ + explicit Matrix(int _rows, int _cols) : MatrixBase<_Scalar, _RowsAtCompile, _ColsAtCompile>(_rows, _cols) { } + + /** + * Destructeur + */ + ~Matrix() { } + + /** + * Opérateur de copie à partir d'une sous-matrice. + * + * Exemple : Matrix B = A.block(i,j,m,n); + */ + template + Matrix& operator= (const SubMatrix<_OtherScalar, OtherRows, _OtherCols, _OtherStorage>& submatrix) + { + // TODO copier les données de la sous-matrice. + // Note : si les dimensions ne correspondent pas, la matrice doit être redimensionnée. + // Vous pouvez présumer qu'il s'agit d'un stockage par colonnes. + return *this; + } + + /** + * Accesseur à une entrée de la matrice (lecture seule) + */ + _Scalar operator()(int i, int j) const + { + // TODO implementer + return (double)(i + j); + } + + /** + * Accesseur à une entrée de la matrice (lecture ou écriture) + */ + _Scalar& operator()(int i, int j) + { + // TODO implementer + // Indice : l'implémentation est identique à celle de la fonction précédente. + _Scalar x = (double)(i + j); + return x; + } + + /** + * Crée une sous-matrice pour un block de taille (rows, cols) à partir de l'index (i,j). + */ + SubMatrix<_Scalar, _RowsAtCompile, _ColsAtCompile, _StorageType> block(int i, int j, int rows, int cols) const + { + return SubMatrix<_Scalar, _RowsAtCompile, _ColsAtCompile, _StorageType>(*this, i, j, rows, cols); + } + + /** + * Calcule l'inverse de la matrice + */ + Matrix inverse() const + { + // Do nothing. + return *this; + } + + /** + * Retourne la transposée de la matrice + */ + template + Matrix<_OtherScalar, _OtherRows, _OtherCols, _OtherStorage> transpose() const + { + // TODO calcule et retourne la transposée de la matrice. + + return Matrix<_OtherScalar, _OtherRows, _OtherCols, _OtherStorage>(); // pas bon, à changer + } + + /** + * Affecte l'identité à la matrice + */ + inline void setIdentity() + { + // TODO affecter la valeur 0.0 partour, sauf sur la diagonale principale où c'est 1.0.. + // Votre implémentation devrait aussi fonctionner pour des matrices qui ne sont pas carrées. + } + + }; + + /** + * Classe Matrix spécialisée pour un stockage par lignes + */ + template + class Matrix< _Scalar, _RowsAtCompile, _ColsAtCompile, RowStorage> : public MatrixBase<_Scalar, _RowsAtCompile, _ColsAtCompile> { + + public: + /** + * Constructeur par défaut + */ + Matrix() : MatrixBase<_Scalar, _RowsAtCompile, _ColsAtCompile>() { } + + /** + * Constructeur de copie + */ + Matrix(const Matrix& other) : MatrixBase<_Scalar, _RowsAtCompile, _ColsAtCompile>(other) { } + + /** + * Constructeur avec spécification du nombre de ligne et de colonnes + */ + explicit Matrix(int rows, int cols) : MatrixBase<_Scalar, _RowsAtCompile, _ColsAtCompile>(rows, cols) { } + + /** + * Destructeur + */ + ~Matrix() { } + + /** + * Opérateur de copie à partir d'une sous-matrice. + * + * Exemple : Matrix B = A.block(i,j,m,n); + */ + template + Matrix& operator= (const SubMatrix<_OtherScalar, OtherRows, _OtherCols, _OtherStorage>& submatrix) + { + // TODO copier les données de la sous-matrice. + // Note : si les dimensions ne correspondent pas, la matrice doit être redimensionnée. + // Vous pouvez présumer qu'il s'agit d'un stockage par lignes. + return *this; + } + + /** + * Accesseur à une entrée de la matrice (lecture seule) + */ + _Scalar operator()(int i, int j) const + { + // TODO implementer + return 0.0; + } + + /** + * Accesseur à une entrée de la matrice (lecture ou écriture) + */ + _Scalar& operator()(int i, int j) + { + // TODO implementer + _Scalar x = 0.0; + return x; + } + + /** + * Crée une sous-matrice pour un block de taille (rows, cols) à partir de l'index (i,j). + */ + SubMatrix<_Scalar, _RowsAtCompile, _ColsAtCompile, RowStorage> block(int i, int j, int rows, int cols) const { + return SubMatrix<_Scalar, _RowsAtCompile, _ColsAtCompile, RowStorage>(*this, i, j, rows, cols); + } + + /** + * Calcule l'inverse de la matrice + */ + Matrix inverse() const + { + // Do nothing. + return *this; + } + + /** + * Retourne la transposée de la matrice + */ + Matrix<_Scalar, _ColsAtCompile, _RowsAtCompile, ColumnStorage> transpose() const + { + // TODO calcule et retourne la transposée de la matrice. + // Optimisez cette fonction en tenant compte du type de stockage utilisé. + + return Matrix<_Scalar, _ColsAtCompile, _RowsAtCompile, ColumnStorage>(); + } + + /** + * Affecte l'identité à la matrice + */ + inline void setIdentity() + { + // TODO affecter la valeur 0.0 partour, sauf sur la diagonale principale où c'est 1.0.. + // Votre implémentation devrait aussi fonctionner pour des matrices qui ne sont pas carrées. + } + + }; + + /** + * Classe pour accéder à une sous-matrice. + * + * Un sous-matrice ne copie pas les données. Au lieu de cela, elle conserve une + * référence à la matrice originale. + */ + template + class SubMatrix + { + private: + // Référence à la matrice originale + Matrix<_Scalar, _RowsAtCompile, _ColsAtCompile, _StorageType>& m_matrix; + + // Constructeur par défaut (privé) + SubMatrix() {} + + // (i,j) est le coin supérieur gauche de la sous-matrice + int m_i; // Décalage en ligne + int m_j; // Décalage en colonne + + // la sous-matrice est de dimension : m_rows x m_cols + int m_rows; // Hauteur de la sous-matrice (nombre de lignes) + int m_cols; // Largeur de la sous-matrice (nombre de colonnes) + + public: + + /** + * Constructeur à partir d'une référence en lecture seule à une matrice. + */ + SubMatrix(const Matrix<_Scalar, _RowsAtCompile, _ColsAtCompile, _StorageType>& _matrix, int _i, int _j, int _rows, int _cols) : + m_matrix(const_cast&>(_matrix)), + m_i(_i), m_j(_j), m_rows(_rows), m_cols(_cols) + { + } + + /** + * Constructeur à partir d'une référence en lecture et écriture à une matrice. + */ + explicit SubMatrix(Matrix<_Scalar, _RowsAtCompile, _ColsAtCompile, _StorageType>& _matrix, int _i, int _j, int _rows, int _cols) : + m_matrix(_matrix), + m_i(_i), m_j(_j), m_rows(_rows), m_cols(_cols) + { + + } + + /** + * Constructeur de copie + */ + SubMatrix(const SubMatrix& other) : + m_matrix(other.m_matrix), + m_i(other.m_i), m_j(other.m_j), m_rows(other.m_rows), m_cols(other.m_cols) + { + } + + /** + * Destructeur + */ + ~SubMatrix() { } + + /** + * Opérateur de copie (à partir d'une matrice) + * + * Copies toutes les entrées de la matrice dans la sous-matrice. + * + * Note : la taille de la matrice doit correspondre à la taille de la + * sous-matrice. + */ + template + SubMatrix& operator= (const Matrix<_OtherScalar, _OtherRows, _OtherCols, _OtherStorage>& matrix) + { + // TODO Cpopie les valeurs de la matrice dans la sous-matrice. + // Note les dimensions de la matrice doivent correspondre à celle de + // la sous-matrice. + return *this; + } + + /** + * Accesseur aux entrées de la sous-matrice (lecture seule) + * + * Note : il faut s'assurer que les indices respectent la taille de la + * sous-matrice + */ + _Scalar operator()(int i, int j) const + { + // TODO implémenter + return 0.0; + } + + /** + * Accesseur aux entrées de la sous-matrice (lecture et écriture) + * + * Note : il faut s'assurer que les indices respectent la taille de la + * sous-matrice + */ + _Scalar& operator()(int i, int j) + { + // TODO implémenter + _Scalar x = 0.0; + return x; + } + + /** + * Retourne la transposée de la sous-matrice sous la forme d'une matrice. + */ + template + Matrix<_OtherScalar, _OtherRows, _OtherCols, _OtherStorage> transpose() const + { + // TODO implémenter + return Matrix<_OtherScalar, _OtherRows, _OtherCols, _OtherStorage>(); + } + + inline int rows() const { return m_rows; } + inline int cols() const { return m_cols; } + + }; + +} diff --git a/labo01/MatrixBase.h b/labo01/MatrixBase.h new file mode 100644 index 0000000..2e5c46f --- /dev/null +++ b/labo01/MatrixBase.h @@ -0,0 +1,357 @@ +#pragma once + +/** + * @file MatrixBase.h + * + * @brief Classe contenant les éléments de base des matrices et des vecteurs. + * + * Nom: + * Code permanent : + * Email : + * + */ + +#include "DenseStorage.h" + +namespace gti320 +{ + + /** + * Classe de base pour les matrices et vecteurs à taille fixe. + */ + template + class MatrixBase + { + protected: + DenseStorage<_Scalar, _Rows* _Cols> m_storage; + + public: + + typedef _Scalar Scalar; + + /** + * Constructeur par défaut + */ + MatrixBase() : m_storage() { } + + /** + * Constructeur de copie + */ + MatrixBase(const MatrixBase& other) : m_storage(other.m_storage) { } + + explicit MatrixBase(int _rows, int _cols) : m_storage() { } + + /** + * Destructeur + */ + ~MatrixBase() { } + + + /** + * Redimensionne la matrice + */ + void resize(int _rows, int _cols) + { + // Ne rien faire. + } + + /** + * Opérateur de copie + */ + MatrixBase& operator=(const MatrixBase& other) + { + if (this != &other) + { + m_storage = other.m_storage; + } + return *this; + } + + inline void setZero() { m_storage.setZero(); } + static inline int cols() { return _Cols; } + static inline int rows() { return _Rows; } + + /** + * Accès à la donnée membre de stockage (en lecture seule) + */ + const DenseStorage<_Scalar, _Rows* _Cols>& storage() const + { + return m_storage; + } + + /** + * Nombre d'éléments stockés dans le tampon. + */ + inline int size() const + { + return m_storage.size(); + } + + /** + * Accès au tampon de données ((lecture seule)) + */ + const _Scalar* data() const + { + return m_storage.data(); + } + + }; + + /** + * Classe de base pour les matrices avec un nombre de lignes dynamique et un + * nombre fixe de colonnes. + */ + template + class MatrixBase<_Scalar, Dynamic, _Cols> + { + protected: + + DenseStorage<_Scalar, Dynamic> m_storage; + int m_rows; + + public: + + typedef _Scalar Scalar; + + /** + * Constructeur par défaut + */ + MatrixBase() : m_storage(), m_rows(0) { } + + explicit MatrixBase(int _rows, int _cols) : m_storage(_rows* _Cols), m_rows(_rows) { } + + /** + * Constructeur de copie + */ + MatrixBase(const MatrixBase& other) : m_storage(other.m_storage), m_rows(other.m_rows) { } + + /** + * Destructeur + */ + ~MatrixBase() { } + + /** + * Opérateur de copie + */ + MatrixBase& operator=(const MatrixBase& other) + { + if (this != &other) + { + m_storage = other.m_storage; + m_rows = other.m_rows; + } + return *this; + } + + /** + * Redimensionne la matrice + */ + void resize(int _rows, int _cols) + { + assert(_cols == _Cols); + m_storage.resize(_rows * _Cols); + m_rows = _rows; + } + + inline void setZero() { m_storage.setZero(); } + + static inline int cols() { return _Cols; } + inline int rows() const { return m_rows; } + + /** + * Accès à la donnée membre de stockage (en lecture seule) + */ + const DenseStorage<_Scalar, Dynamic>& storage() const + { + return m_storage; + } + + /** + * Nombre d'éléments stockés dans le tampon. + */ + inline int size() const + { + return m_storage.size(); + } + + /** + * Accès au tampon de données ((lecture seule)) + */ + const _Scalar* data() const + { + return m_storage.data(); + } + }; + + /** + * Classe de base pour les matrices avec un nombre fixe de lignes et un + * nombre de colonnes dynamique. + */ + template + class MatrixBase<_Scalar, _Rows, Dynamic> + { + protected: + + DenseStorage<_Scalar, Dynamic> m_storage; + int m_cols; + + public: + + typedef _Scalar Scalar; + + /** + * Constructeur par défaut + */ + MatrixBase() : m_storage() { } + + explicit MatrixBase(int _rows, int _cols) : m_storage(_rows* _cols), m_cols(_cols) { } + + /** + * Constructeur de copie + */ + MatrixBase(const MatrixBase& other) : m_storage(other.m_storage), m_cols(other.m_cols) { } + + /** + * Destructeur + */ + ~MatrixBase() { } + + /** + * Opérateur de copie + */ + MatrixBase& operator=(const MatrixBase& other) + { + if (this != &other) { + m_storage = other.m_storage; + m_cols = other.m_cols; + } + + return *this; + } + + /** + * Redimensionne la matrice + */ + void resize(int _rows, int _cols) + { + assert(_rows == _Rows); + m_storage.resize(_Rows * _cols); + m_cols = _cols; + } + + inline void setZero() { m_storage.setZero(); } + + inline int cols() const { return m_cols; } + static inline int rows() { return _Rows; } + + /** + * Accès à la donnée membre de stockage (en lecture seule) + */ + const DenseStorage<_Scalar, Dynamic>& storage() const + { + return m_storage; + } + + /** + * Nombre d'éléments stockés dans le tampon. + */ + inline int size() const + { + return m_storage.size(); + } + + /** + * Accès au tampon de données ((lecture seule)) + */ + const _Scalar* data() const + { + return m_storage.data(); + } + }; + + /** + * Classe de base pour les matrices avec un nombre de lignes et de colonnes + * dynamiques. + */ + template + class MatrixBase<_Scalar, Dynamic, Dynamic> + { + protected: + + DenseStorage<_Scalar, Dynamic> m_storage; + int m_cols; + int m_rows; + + public: + typedef _Scalar Scalar; + + /** + * Constructeur par défaut + */ + MatrixBase() : m_storage(), m_rows(0), m_cols(0) { } + + explicit MatrixBase(int _rows, int _cols) : m_storage(_rows* _cols), m_rows(_rows), m_cols(_cols) { } + + /** + * Constructeur de copie + */ + MatrixBase(const MatrixBase& other) : m_storage(other.m_storage), m_rows(other.m_rows), m_cols(other.m_cols) { } + + /** + * Destructeur + */ + ~MatrixBase() { } + + /** + * Opérateur de copie + */ + MatrixBase& operator=(const MatrixBase& other) + { + if (this != &other) + { + resize(other.m_rows, other.m_cols); + m_storage = other.m_storage; + } + return *this; + } + + /** + * Redimensionne la matrice + */ + void resize(int _rows, int _cols) + { + m_storage.resize(_rows * _cols); + m_rows = _rows; + m_cols = _cols; + } + + inline void setZero() { m_storage.setZero(); } + + inline int cols() const { return m_cols; } + inline int rows() const { return m_rows; } + + /** + * Accès à la donnée membre de stockage (en lecture seule) + */ + const DenseStorage<_Scalar, Dynamic>& storage() const + { + return m_storage; + } + + /** + * Nombre d'éléments stockés dans le tampon. + */ + inline int size() const + { + return m_storage.size(); + } + + /** + * Accès au tampon de données ((lecture seule)) + */ + const _Scalar* data() const + { + return m_storage.data(); + } + }; +} diff --git a/labo01/Operators.h b/labo01/Operators.h new file mode 100644 index 0000000..d4c438f --- /dev/null +++ b/labo01/Operators.h @@ -0,0 +1,179 @@ +#pragma once + +/** + * @file Operators.h + * + * @brief Opérateurs arithmétiques pour les matrices et les vecteurs. + * + * Nom: + * Code permanent : + * Email : + * + */ + +#include "Matrix.h" +#include "Vector.h" + + /** + * Implémentation de divers opérateurs arithmétiques pour les matrices et les vecteurs. + */ +namespace gti320 { + + /** + * Multiplication : Matrice * Matrice (générique) + */ + template + Matrix<_Scalar, RowsA, ColsB> operator*(const Matrix<_Scalar, RowsA, ColsA, StorageA>& A, const Matrix<_Scalar, RowsB, ColsB, StorageB>& B) + { + // TODO implémenter + return Matrix<_Scalar, RowsA, ColsB>(); + } + + /** + * Multiplication : Matrice (colonne) * Matrice (ligne) + * + * Spécialisation de l'opérateur de multiplication pour le cas où les matrices + * ont un stockage à taille dynamique et où la matrice de gauche utilise un + * stockage par colonnes et celle de droite un stockage par lignes. + */ + template + Matrix<_Scalar, Dynamic, Dynamic> operator*(const Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage>& A, const Matrix<_Scalar, Dynamic, Dynamic, RowStorage>& B) + { + // TODO : implémenter + return Matrix<_Scalar, Dynamic, Dynamic>(); + } + + /** + * Multiplication : Matrice (ligne) * Matrice (colonne) + * + * Spécialisation de l'opérateur de multiplication pour le cas où les matrices + * ont un stockage à taille dynamique et où la matrice de gauche utilise un + * stockage par lignes et celle de droite un stockage par colonnes. + */ + template + Matrix<_Scalar, Dynamic, Dynamic> operator*(const Matrix<_Scalar, Dynamic, Dynamic, RowStorage>& A, const Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage>& B) + { + // TODO : implémenter + return Matrix<_Scalar, Dynamic, Dynamic>(); + } + + + /** + * Addition : Matrice + Matrice (générique) + */ + template + Matrix<_Scalar, Rows, Cols> operator+(const Matrix<_Scalar, Rows, Cols, StorageA>& A, const Matrix<_Scalar, Rows, Cols, StorageB>& B) + { + // TODO : implémenter + return Matrix<_Scalar, Rows, Cols>(); + } + + /** + * Addition : Matrice (colonne) + Matrice (colonne) + * + * Spécialisation de l'opérateur d'addition pour le cas où les deux matrices + * sont stockées par colonnes. + */ + template + Matrix<_Scalar, Dynamic, Dynamic> operator+(const Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage>& A, const Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage>& B) + { + // TODO : implémenter + return Matrix<_Scalar, Dynamic, Dynamic>(); + } + + /** + * Addition : Matrice (ligne) + Matrice (ligne) + * + * Spécialisation de l'opérateur d'addition pour le cas où les deux matrices + * sont stockées par lignes. + */ + template + Matrix<_Scalar, Dynamic, Dynamic, RowStorage> operator+(const Matrix<_Scalar, Dynamic, Dynamic, RowStorage>& A, const Matrix<_Scalar, Dynamic, Dynamic, RowStorage>& B) + { + // TODO : implémenter + return Matrix<_Scalar, Dynamic, Dynamic, RowStorage>(); + } + + /** + * Multiplication : Scalaire * Matrice (colonne) + * + * Spécialisation de l'opérateur de multiplication par un scalaire pour le + * cas d'une matrice stockée par colonnes. + */ + template + Matrix<_Scalar, _Rows, _Cols, ColumnStorage> operator*(const _Scalar& a, const Matrix<_Scalar, _Rows, _Cols, ColumnStorage>& A) + { + // TODO : implémenter + return Matrix<_Scalar, Dynamic, Dynamic>(); + } + + /** + * Multiplication : Scalaire * Matrice (ligne) + * + * Spécialisation de l'opérateur de multiplication par un scalaire pour le + * cas d'une matrice stockée par lignes. + */ + template + Matrix<_Scalar, _Rows, _Cols, RowStorage> operator*(const _Scalar& a, const Matrix<_Scalar, _Rows, _Cols, RowStorage>& A) + { + // TODO : implémenter + return Matrix<_Scalar, Dynamic, Dynamic, RowStorage>(); + } + + /** + * Multiplication : Matrice (ligne) * Vecteur + * + * Spécialisation de l'opérateur de multiplication matrice*vecteur pour le + * cas où la matrice est représentée par lignes. + */ + template + Vector<_Scalar, _Rows> operator*(const Matrix<_Scalar, _Rows, _Cols, RowStorage>& A, const Vector<_Scalar, _Cols>& v) + { + // TODO : implémenter + return Vector<_Scalar, _Rows>(); + } + + /** + * Multiplication : Matrice (colonne) * Vecteur + * + * Spécialisation de l'opérateur de multiplication matrice*vecteur pour le + * cas où la matrice est représentée par colonnes. + */ + template + Vector<_Scalar, _Rows> operator*(const Matrix<_Scalar, _Rows, _Cols, ColumnStorage>& A, const Vector<_Scalar, _Cols>& v) + { + // TODO : implémenter + return Vector<_Scalar, _Rows>(); + } + + /** + * Multiplication : Scalaire * Vecteur + */ + template + Vector<_Scalar, _Rows> operator*(const _Scalar& a, const Vector<_Scalar, _Rows>& v) + { + // TODO : implémenter + return Vector<_Scalar, _Rows>(); + } + + + /** + * Addition : Vecteur + Vecteur + */ + template + Vector<_Scalar, _RowsA> operator+(const Vector<_Scalar, _RowsA>& a, const Vector<_Scalar, _RowsB>& b) + { + // TODO : implémenter + return Vector<_Scalar, _RowsA>(); + } + + /** + * Soustraction : Vecteur - Vecteur + */ + template + Vector<_Scalar, _RowsA> operator-(const Vector<_Scalar, _RowsA>& a, const Vector<_Scalar, _RowsB>& b) + { + // TODO : implémenter + return Vector<_Scalar, _RowsA>(); + } +} diff --git a/labo01/Vector.h b/labo01/Vector.h new file mode 100644 index 0000000..11ef602 --- /dev/null +++ b/labo01/Vector.h @@ -0,0 +1,104 @@ +#pragma once + +/** + * @file Vector.h + * + * @brief Implémentation de vecteurs simples + * + * Nom: + * Code permanent : + * Email : + * + */ + +#include +#include "MatrixBase.h" + +namespace gti320 { + + /** + * Classe vecteur générique. + * + * Cette classe réutilise la classe `MatrixBase` et ses spécialisations de + * templates pour les manipulation bas niveau. + */ + template + class Vector : public MatrixBase<_Scalar, _Rows, 1> { + public: + + /** + * Constructeur par défaut + */ + Vector() : MatrixBase<_Scalar, _Rows, 1>() { } + + /** + * Contructeur à partir d'un taille (rows). + */ + explicit Vector(int rows) : MatrixBase<_Scalar, _Rows, 1>(rows, 1) { } + + /** + * Constructeur de copie + */ + Vector(const Vector& other) : MatrixBase<_Scalar, _Rows, 1>(other) { } + + /** + * Destructeur + */ + ~Vector() { } + + /** + * Opérateur de copie + */ + Vector& operator=(const Vector& other) + { + // TODO implémenter + this->m_storage = other.m_storage; + return *this; + } + + /** + * Accesseur à une entrée du vecteur (lecture seule) + */ + _Scalar operator()(int i) const + { + // TODO implémenter + return (double)i; + } + + /** + * Accesseur à une entrée du vecteur (lecture et écriture) + */ + _Scalar& operator()(int i) + { + // TODO implémenter + _Scalar x = (double)i; + return x; + } + + /** + * Modifie le nombre de lignes du vecteur + */ + void resize(int _rows) + { + MatrixBase<_Scalar, _Rows, 1>::resize(_rows, 1); + } + + /** + * Produit scalaire de *this et other. + */ + inline _Scalar dot(const Vector& other) const + { + // TODO implémenter + return 0.0; + } + + /** + * Retourne la norme euclidienne du vecteur + */ + inline _Scalar norm() const + { + // TODO implémenter + return 0.0; + } + }; +} diff --git a/labo01/main.cpp b/labo01/main.cpp new file mode 100644 index 0000000..92fb279 --- /dev/null +++ b/labo01/main.cpp @@ -0,0 +1,520 @@ +/** + * @file main.cpp + * + * @brief Unit tests for a simple linear algebra library. + * + * Nom: William Nolin + * Code permanent : NOLW76060101 + * Email : william.nolin.1@ens.etsmtl.ca + * + */ + +#include "Matrix.h" +#include "Vector.h" +#include "Math3D.h" +#include "Operators.h" + +#include +#include + +using namespace gti320; + +/** + * Multiplication matrice * vecteur, utilisant une implémentation naive + */ +template +static inline Vector<_Scalar, Dynamic> naiveMatrixMult(const Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage>& A, const Vector<_Scalar, Dynamic>& v) +{ + assert(A.cols() == v.rows()); + + Vector<_Scalar, Dynamic> b(A.rows()); + assert(b.rows() == A.rows()); + + for (int i = 0; i < A.rows(); ++i) { + b(i) = 0.0; + for (int j = 0; j < A.cols(); ++j) { + b(i) += A(i, j) * v(j); + } + } + + return b; +} + +/** + * Addition matrice + matrice, utilisant une implémentation naive + */ +template +static inline Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage> naiveMatrixAddition(const Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage>& A, const Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage>& B) +{ + assert(A.cols() == B.cols() && A.rows() == B.rows()); + + Matrix<_Scalar, Dynamic, Dynamic, ColumnStorage> C(A.rows(), A.cols()); + assert(C.rows() == A.rows() && C.cols() == A.cols()); + for (int i = 0; i < C.rows(); ++i) { + for (int j = 0; j < C.cols(); ++j) { + C(i, j) = A(i, j) + B(i, j); + } + } + return C; +} + +/** + * Multiplication matrice * matrice, utilisant une implémentation naive. + */ +template +static inline Matrix<_Scalar, Dynamic, Dynamic, _Storage> naiveMatrixMult(const Matrix<_Scalar, Dynamic, Dynamic, _Storage>& A, const Matrix<_Scalar, Dynamic, Dynamic, _Storage>& B) +{ + assert(A.cols() == B.rows()); + Matrix<_Scalar, Dynamic, Dynamic> product(A.rows(), B.cols()); + for (int i = 0; i < A.rows(); ++i) + { + for (int j = 0; j < B.cols(); ++j) + { + for (int k = 0; k < A.cols(); ++k) + { + product(i, j) += A(i, k) * B(k, j); + } + } + } + return product; +} + +// Test les matrice avec redimensionnement dynamique +TEST(TestLabo1, DynamicMatrixTests) +{ + // Crée une matrice à taille dynamique + // (note : les valeurs par défaut du patron de la classe `Matrix` mettent le + // le nombre de ligne et de colonnes à `Dynamic`) + Matrix M(3, 5); + EXPECT_EQ(M.cols(), 5); + EXPECT_EQ(M.rows(), 3); + + // Redimensionne la matrice + M.resize(100, 1000); + EXPECT_EQ(M.cols(), 1000); + EXPECT_EQ(M.rows(), 100); + + // Test - stockage par colonnes + Matrix ColM(100, 100); + ColM.setZero(); + ColM(0, 0) = 1.0; + ColM(99, 99) = 99.0; + ColM(10, 33) = 5.0; + EXPECT_EQ(ColM(0, 0), 1.0); + EXPECT_EQ(ColM(10, 33), 5.0); + EXPECT_EQ(ColM(99, 99), 99.0); + + // Test - stockage par lignes + Matrix RowM(5, 4); + RowM.setZero(); + RowM(0, 0) = 2.1; + RowM(3, 3) = -0.2; + RowM(4, 3) = 1.2; + EXPECT_EQ(RowM.rows(), 5); + EXPECT_EQ(RowM.cols(), 4); + EXPECT_DOUBLE_EQ(RowM(0, 0), 2.1); + EXPECT_DOUBLE_EQ(RowM(3, 3), -0.2); + EXPECT_DOUBLE_EQ(RowM(4, 3), 1.2); + EXPECT_DOUBLE_EQ(RowM(3, 2), 0.0); + + // Transposée + const auto RowMT = RowM.transpose(); + EXPECT_EQ(RowMT.rows(), 4); + EXPECT_EQ(RowMT.cols(), 5); + EXPECT_DOUBLE_EQ(RowMT(0, 0), 2.1); + EXPECT_DOUBLE_EQ(RowMT(3, 3), -0.2); + EXPECT_DOUBLE_EQ(RowMT(3, 4), 1.2); + EXPECT_DOUBLE_EQ(RowMT(2, 3), 0.0); +} + + + +/** + * Test pour les vecteurs à taille dynamique + */ +TEST(TestLabo1, DynamicVectorSizeTest) +{ + Vector v(5); + v.setZero(); + + EXPECT_EQ(v.rows(), 5); + + v.resize(3); + EXPECT_EQ(v.rows(), 3); + + v(0) = 1.0; + v(1) = 2.0; + v(2) = 3.0; + + EXPECT_DOUBLE_EQ(v.norm(), 3.7416573867739413855837487323165); + + Vector v2(3); + v2.setZero(); + v2(1) = 2.0; + + EXPECT_DOUBLE_EQ(v2.dot(v), 4.0); + EXPECT_DOUBLE_EQ(v2(0), 0.0); + EXPECT_DOUBLE_EQ(v2(1), 2.0); + EXPECT_DOUBLE_EQ(v2(2), 0.0); +} + +/** + * Test pour les matrice à taille fixe + */ +TEST(TestLabo1, Matrix4x4SizeTest) +{ + Matrix4d M; + M.setZero(); + + EXPECT_EQ(M.cols(), 4); + EXPECT_EQ(M.rows(), 4); +} + +/** + * Test pour les opérateurs d'arithmétique matricielle. + */ +TEST(TestLabo1, MatrixMatrixOperators) +{ + // Opérations arithmétiques avec matrices à taille dynamique + { + // Test : matrice identité + Matrix A(6, 6); + A.setIdentity(); + EXPECT_DOUBLE_EQ(A(0, 0), 1.0); + EXPECT_DOUBLE_EQ(A(1, 1), 1.0); + EXPECT_DOUBLE_EQ(A(2, 2), 1.0); + EXPECT_DOUBLE_EQ(A(3, 3), 1.0); + EXPECT_DOUBLE_EQ(A(4, 4), 1.0); + EXPECT_DOUBLE_EQ(A(5, 5), 1.0); + EXPECT_DOUBLE_EQ(A(0, 1), 0.0); + EXPECT_DOUBLE_EQ(A(1, 0), 0.0); + + // Test : produit scalaire * matrice + const double alpha = 2.5; + Matrix B = alpha * A; + EXPECT_DOUBLE_EQ(B(0, 0), alpha); + EXPECT_DOUBLE_EQ(B(1, 1), alpha); + EXPECT_DOUBLE_EQ(B(2, 2), alpha); + EXPECT_DOUBLE_EQ(B(3, 3), alpha); + EXPECT_DOUBLE_EQ(B(4, 4), alpha); + EXPECT_DOUBLE_EQ(B(5, 5), alpha); + EXPECT_DOUBLE_EQ(B(0, 1), 0.0); + EXPECT_DOUBLE_EQ(B(1, 0), 0.0); + + // Test : produit matrice * matrice + Matrix C = A * B; + EXPECT_DOUBLE_EQ(C(0, 0), A(0, 0) * B(0, 0)); + EXPECT_DOUBLE_EQ(C(1, 1), A(1, 1) * B(1, 1)); + EXPECT_DOUBLE_EQ(C(2, 2), A(2, 2) * B(2, 2)); + EXPECT_DOUBLE_EQ(C(3, 3), A(3, 3) * B(3, 3)); + EXPECT_DOUBLE_EQ(C(4, 4), A(4, 4) * B(4, 4)); + EXPECT_DOUBLE_EQ(C(5, 5), A(5, 5) * B(5, 5)); + EXPECT_DOUBLE_EQ(C(0, 1), 0.0); + EXPECT_DOUBLE_EQ(C(2, 3), 0.0); + + // Test : addition matrice * matrice + Matrix A_plus_B = A + B; + EXPECT_DOUBLE_EQ(A_plus_B(0, 0), A(0, 0) + B(0, 0)); + EXPECT_DOUBLE_EQ(A_plus_B(1, 1), A(1, 1) + B(1, 1)); + EXPECT_DOUBLE_EQ(A_plus_B(2, 2), A(2, 2) + B(2, 2)); + EXPECT_DOUBLE_EQ(A_plus_B(3, 3), A(3, 3) + B(3, 3)); + EXPECT_DOUBLE_EQ(A_plus_B(4, 4), A(4, 4) + B(4, 4)); + EXPECT_DOUBLE_EQ(A_plus_B(5, 5), A(5, 5) + B(5, 5)); + EXPECT_DOUBLE_EQ(A_plus_B(0, 1), 0.0); + EXPECT_DOUBLE_EQ(A_plus_B(2, 3), 0.0); + } + + // Opérations arithmétique avec matrices à stockage par lignes et par + // colonnes. + { + // Création d'un matrice à stockage par lignes + Matrix A(5, 5); + A(0, 0) = 0.8147; A(0, 1) = 0.0975; A(0, 2) = 0.1576; A(0, 3) = 0.1419; A(0, 4) = 0.6557; + A(1, 0) = 0.9058; A(1, 1) = 0.2785; A(1, 2) = 0.9706; A(1, 3) = 0.4218; A(1, 4) = 0.0357; + A(2, 0) = 0.1270; A(2, 1) = 0.5469; A(2, 2) = 0.9572; A(2, 3) = 0.9157; A(2, 4) = 0.8491; + A(3, 0) = 0.9134; A(3, 1) = 0.9575; A(3, 2) = 0.4854; A(3, 3) = 0.7922; A(3, 4) = 0.9340; + A(4, 0) = 0.6324; A(4, 1) = 0.9649; A(4, 2) = 0.8003; A(4, 3) = 0.9595; A(4, 4) = 0.6787; + + // Test : transposée (le résultat est une matrice à stockage par + // colonnes) + Matrix B = A.transpose(); + + // Test : multiplication matrix(ligne) * matrice(colonne) + // Note : teste seulement la première et la dernière colonne + const auto C = A * B; + EXPECT_NEAR(C(0, 0), 1.14815820000000, 1e-3); EXPECT_NEAR(C(0, 4), 1.31659795000000, 1e-3); + EXPECT_NEAR(C(1, 0), 1.00133748000000, 1e-3); EXPECT_NEAR(C(1, 4), 2.04727044000000, 1e-3); + EXPECT_NEAR(C(2, 0), 0.99433707000000, 1e-3); EXPECT_NEAR(C(2, 4), 2.82896409000000, 1e-3); + EXPECT_NEAR(C(3, 0), 1.63883925000000, 1e-3); EXPECT_NEAR(C(3, 4), 3.28401323000000, 1e-3); + EXPECT_NEAR(C(4, 0), 1.31659795000000, 1e-3); EXPECT_NEAR(C(4, 4), 3.35271580000000, 1e-3); + + + // Test : multiplication matrice(colonne) * matrice(ligne) + // Note : teste seulement la première et la dernière colonne + const auto C2 = B * A; + EXPECT_NEAR(C2(0, 0), 2.73456805000000, 1e-3); EXPECT_NEAR(C2(0, 4), 1.95669703000000, 1e-3); + EXPECT_NEAR(C2(1, 0), 1.88593811000000, 1e-3); EXPECT_NEAR(C2(1, 4), 2.08742862000000, 1e-3); + EXPECT_NEAR(C2(2, 0), 2.07860468000000, 1e-3); EXPECT_NEAR(C2(2, 4), 1.94727447000000, 1e-3); + EXPECT_NEAR(C2(3, 0), 1.94434955000000, 1e-3); EXPECT_NEAR(C2(3, 4), 2.27675041000000, 1e-3); + EXPECT_NEAR(C2(4, 0), 1.95669703000000, 1e-3); EXPECT_NEAR(C2(4, 4), 2.48517748000000, 1e-3); + + // Test : addition matrice(ligne) + matrice(ligne) + // Note : teste seulement la première et la dernière colonne + const auto A_plus_A = A + A; + EXPECT_DOUBLE_EQ(A_plus_A(0, 0), A(0, 0) + A(0, 0)); EXPECT_DOUBLE_EQ(A_plus_A(0, 4), A(0, 4) + A(0, 4)); + EXPECT_DOUBLE_EQ(A_plus_A(1, 0), A(1, 0) + A(1, 0)); EXPECT_DOUBLE_EQ(A_plus_A(1, 4), A(1, 4) + A(1, 4)); + EXPECT_DOUBLE_EQ(A_plus_A(2, 0), A(2, 0) + A(2, 0)); EXPECT_DOUBLE_EQ(A_plus_A(2, 4), A(2, 4) + A(2, 4)); + EXPECT_DOUBLE_EQ(A_plus_A(3, 0), A(3, 0) + A(3, 0)); EXPECT_DOUBLE_EQ(A_plus_A(3, 4), A(3, 4) + A(3, 4)); + EXPECT_DOUBLE_EQ(A_plus_A(4, 0), A(4, 0) + A(4, 0)); EXPECT_DOUBLE_EQ(A_plus_A(4, 4), A(4, 4) + A(4, 4)); + + // Test : addition matrice(colonne) + matrice(colonne) + // Note : teste seulement la première et la dernière colonne + const auto B_plus_B = B + B; + EXPECT_DOUBLE_EQ(B_plus_B(0, 0), B(0, 0) + B(0, 0)); EXPECT_DOUBLE_EQ(B_plus_B(0, 4), B(0, 4) + B(0, 4)); + EXPECT_DOUBLE_EQ(B_plus_B(1, 0), B(1, 0) + B(1, 0)); EXPECT_DOUBLE_EQ(B_plus_B(1, 4), B(1, 4) + B(1, 4)); + EXPECT_DOUBLE_EQ(B_plus_B(2, 0), B(2, 0) + B(2, 0)); EXPECT_DOUBLE_EQ(B_plus_B(2, 4), B(2, 4) + B(2, 4)); + EXPECT_DOUBLE_EQ(B_plus_B(3, 0), B(3, 0) + B(3, 0)); EXPECT_DOUBLE_EQ(B_plus_B(3, 4), B(3, 4) + B(3, 4)); + EXPECT_DOUBLE_EQ(B_plus_B(4, 0), B(4, 0) + B(4, 0)); EXPECT_DOUBLE_EQ(B_plus_B(4, 4), B(4, 4) + B(4, 4)); + + } +} + +/** + * Test pour la multiplication matrice * vecteur + */ +TEST(TestLabo1, MatrixVectorOperators) +{ + // Vecteur à taille dynamique + Vector v(5); + v(0) = 1.0; + v(1) = 2.0; + v(2) = 4.0; + v(3) = 8.0; + v(4) = 16.0; + + // Test : multiplication par la matrice identité + { + Matrix M(5, 5); + M.setIdentity(); + + const auto b = M * v; + EXPECT_DOUBLE_EQ(b(0), 1.0); + EXPECT_DOUBLE_EQ(b(1), 2.0); + EXPECT_DOUBLE_EQ(b(2), 4.0); + EXPECT_DOUBLE_EQ(b(3), 8.0); + EXPECT_DOUBLE_EQ(b(4), 16.0); + } + + // Test : multiplication par une matrice à taille dynamique avec stockage par ligne. + { + Matrix M(5, 5); + M.setIdentity(); + M = 2.0 * M; + + Vector b2 = M * v; + EXPECT_DOUBLE_EQ(b2(0), 2.0); + EXPECT_DOUBLE_EQ(b2(1), 4.0); + EXPECT_DOUBLE_EQ(b2(2), 8.0); + EXPECT_DOUBLE_EQ(b2(3), 16.0); + EXPECT_DOUBLE_EQ(b2(4), 32.0); + } +} + +/** + * Opérateurs d'arithmétique vectorielle + */ +TEST(TestLabo1, VectorOperators) +{ + Vector v(5); + v(0) = 0.1; + v(1) = 0.2; + v(2) = 0.4; + v(3) = 0.8; + v(4) = 1.6; + + // Test : multiplication scalaire * vecteur + const double alpha = 4.0; + const auto v2 = alpha * v; + EXPECT_DOUBLE_EQ(v2(0), alpha * v(0)); + EXPECT_DOUBLE_EQ(v2(1), alpha * v(1)); + EXPECT_DOUBLE_EQ(v2(2), alpha * v(2)); + EXPECT_DOUBLE_EQ(v2(3), alpha * v(3)); + EXPECT_DOUBLE_EQ(v2(4), alpha * v(4)); + + // Test : addition vecteur + vecteur + const auto v3 = v + v2; + EXPECT_DOUBLE_EQ(v3(0), v(0) + v2(0)); + EXPECT_DOUBLE_EQ(v3(1), v(1) + v2(1)); + EXPECT_DOUBLE_EQ(v3(2), v(2) + v2(2)); + EXPECT_DOUBLE_EQ(v3(3), v(3) + v2(3)); + EXPECT_DOUBLE_EQ(v3(4), v(4) + v2(4)); +} + + +/** + * Mathématiques 3D + */ +TEST(TestLabo1, Math3D) +{ + // Test : norme d'un vecteur de dimension 3 + Vector3d v; + v.setZero(); + v(1) = 2.0; + EXPECT_EQ(v.rows(), 3); + EXPECT_EQ(v.cols(), 1); + EXPECT_DOUBLE_EQ(v(0), 0.0); + EXPECT_DOUBLE_EQ(v(1), 2.0); + EXPECT_DOUBLE_EQ(v(2), 0.0); + EXPECT_DOUBLE_EQ(v.norm(), 2.0); + + // Test : calcul de la norme d'un deuxième vecteur 3D + Vector3d v2; + v2(0) = 4.0; + v2(1) = 2.0; + v2(2) = 5.0; + EXPECT_EQ(v2.rows(), 3); + EXPECT_EQ(v2.cols(), 1); + EXPECT_DOUBLE_EQ(v2(0), 4.0); + EXPECT_DOUBLE_EQ(v2(1), 2.0); + EXPECT_DOUBLE_EQ(v2(2), 5.0); + EXPECT_DOUBLE_EQ(v2.norm(), 6.7082039324993690892275210061938); + + // Test : produit scalaire + EXPECT_DOUBLE_EQ(v.dot(v2), 4.0); + + // Test : matrice identité 4x4 + Matrix4d M; + M.setIdentity(); + EXPECT_DOUBLE_EQ(M(0, 0), 1.0); + EXPECT_DOUBLE_EQ(M(0, 1), 0.0); + EXPECT_DOUBLE_EQ(M(0, 2), 0.0); + EXPECT_DOUBLE_EQ(M(1, 1), 1.0); + EXPECT_DOUBLE_EQ(M(1, 0), 0.0); + EXPECT_DOUBLE_EQ(M(1, 2), 0.0); + EXPECT_DOUBLE_EQ(M(2, 0), 0.0); + EXPECT_DOUBLE_EQ(M(2, 1), 0.0); + EXPECT_DOUBLE_EQ(M(2, 2), 1.0); + + // Test : création d'une matrice de rotation de 45 degrés autour de l'axe des x + const auto Rx = makeRotation(M_PI / 4.0, 0, 0); + EXPECT_NEAR(Rx(0, 0), 1, 1e-3); EXPECT_NEAR(Rx(0, 1), 0, 1e-3); EXPECT_NEAR(Rx(0, 2), 0, 1e-3); + EXPECT_NEAR(Rx(1, 0), 0, 1e-3); EXPECT_NEAR(Rx(1, 1), 0.7071, 1e-3); EXPECT_NEAR(Rx(1, 2), -0.7071, 1e-3); + EXPECT_NEAR(Rx(2, 0), 0, 1e-3); EXPECT_NEAR(Rx(2, 1), 0.7071, 1e-3); EXPECT_NEAR(Rx(2, 2), 0.7071, 1e-3); + + // Test : création d'une matrice de rotation de 45 degrés autour de l'axe des y + const auto Ry = makeRotation(0, M_PI / 4.0, 0); + EXPECT_NEAR(Ry(0, 0), 0.7071, 1e-3); EXPECT_NEAR(Ry(0, 1), 0, 1e-3); EXPECT_NEAR(Ry(0, 2), 0.7071, 1e-3); + EXPECT_NEAR(Ry(1, 0), 0, 1e-3); EXPECT_NEAR(Ry(1, 1), 1, 1e-3); EXPECT_NEAR(Ry(1, 2), 0, 1e-3); + EXPECT_NEAR(Ry(2, 0), -0.7071, 1e-3); EXPECT_NEAR(Ry(2, 1), 0, 1e-3); EXPECT_NEAR(Ry(2, 2), 0.7071, 1e-3); + + // Test : création d'une matrice de rotation de 45 degrés autour de l'axe des z + const auto Rz = makeRotation(0, 0, M_PI / 4.0); + EXPECT_NEAR(Rz(0, 0), 0.7071, 1e-3); EXPECT_NEAR(Rz(0, 1), -0.7071, 1e-3); EXPECT_NEAR(Rz(0, 2), 0, 1e-3); + EXPECT_NEAR(Rz(1, 0), 0.7071, 1e-3); EXPECT_NEAR(Rz(1, 1), 0.7071, 1e-3); EXPECT_NEAR(Rz(1, 2), 0, 1e-3); + EXPECT_NEAR(Rz(2, 0), 0, 1e-3); EXPECT_NEAR(Rz(2, 1), 0, 1e-3); EXPECT_NEAR(Rz(2, 2), 1, 1e-3); + + // Test : création d'une matrice de rotation quelconque. + const auto Rxyz = makeRotation(M_PI / 3.0, -M_PI / 6.0, M_PI / 4.0); + EXPECT_NEAR(Rxyz(0, 0), 0.6124, 1e-3); EXPECT_NEAR(Rxyz(0, 1), -0.6597, 1e-3); EXPECT_NEAR(Rxyz(0, 2), 0.4356, 1e-3); + EXPECT_NEAR(Rxyz(1, 0), 0.6124, 1e-3); EXPECT_NEAR(Rxyz(1, 1), 0.0474, 1e-3); EXPECT_NEAR(Rxyz(1, 2), -0.7891, 1e-3); + EXPECT_NEAR(Rxyz(2, 0), 0.5, 1e-3); EXPECT_NEAR(Rxyz(2, 1), 0.75, 1e-3); EXPECT_NEAR(Rxyz(2, 2), 0.4330, 1e-3); + + // Test : création d'une transformation homogène via la sous-matrice 3x3 en + // utilisant la fonction `block` + M.block(0, 0, 3, 3) = Rxyz; + M(0, 3) = -0.1; + M(1, 3) = 1.0; + M(2, 3) = 2.1; + + // Test : calcule l'inverse de la matrice M et vérifie que M^(-1) * M * v = v + const Matrix4d Minv = M.inverse(); + const Vector3d v3 = Minv * (M * v2); + EXPECT_DOUBLE_EQ(v3(0), v2(0)); + EXPECT_DOUBLE_EQ(v3(1), v2(1)); + EXPECT_DOUBLE_EQ(v3(2), v2(2)); + + // Test : translation d'un vecteur 3D effectuée avec une matrice 4x4 en coordonnées homogènes + Matrix4d T; + T.setIdentity(); + T(0, 3) = 1.2; + T(1, 3) = 2.5; + T(2, 3) = -4.0; + const Vector3d t = T * v3; + EXPECT_DOUBLE_EQ(t(0), v3(0) + 1.2); + EXPECT_DOUBLE_EQ(t(1), v3(1) + 2.5); + EXPECT_DOUBLE_EQ(t(2), v3(2) - 4.0); + + // Test : inverse d'un matrice de rotation + const Matrix3d Rinv = Rxyz.inverse(); + const Matrix3d RT = Rxyz.transpose(); + EXPECT_DOUBLE_EQ(Rinv(0, 0), RT(0, 0)); + EXPECT_DOUBLE_EQ(Rinv(1, 1), RT(1, 1)); + EXPECT_DOUBLE_EQ(Rinv(0, 2), RT(0, 2)); + + +} + +/** + * Test des performance de la multiplication matrice * vecteur + * pour de grandes dimensions. + */ +TEST(TestLabo1, PerformanceMatrixVector) +{ + Matrix A(16384, 16384); // grande matrice avec stockage colonne + Vector v(16384); // grand vecteur + + using namespace std::chrono; + // Test : multiplication avec l'algorithme naif. + high_resolution_clock::time_point t = high_resolution_clock::now(); + naiveMatrixMult(A, v); + const duration naive_t = duration_cast>(high_resolution_clock::now() - t); + + // Test : multiplication avec l'implémentation spécifique pour les matrices avec + // stockage par colonnes. + t = high_resolution_clock::now(); + A* v; + const duration optimal_t = duration_cast>(high_resolution_clock::now() - t); + + EXPECT_TRUE(optimal_t < 0.4 * naive_t) + << "Naive time: " << duration_cast(naive_t).count() << " ms, " + << "optimized time: " << duration_cast(optimal_t).count() << " ms"; +} + +/** + * Test des performances de l'addition matrice + matrice + * pour de grandes dimensions. + */ +TEST(TestLabo1, PerformanceLargeMatrixMatrix) +{ + // deux grandes matrices à stockage par colonnes + Matrix A(16384, 16384); + Matrix B(16384, 16384); + + using namespace std::chrono; + high_resolution_clock::time_point t = high_resolution_clock::now(); + // Test : addition avec l'algorithme naif + naiveMatrixAddition(A, B); + const duration naive_t = duration_cast>(high_resolution_clock::now() - t); + + // Test : addition avec l'implémentation spécifique pour les matrices à + // stockage par colonnes. + t = high_resolution_clock::now(); + A + B; + const duration optimal_t = duration_cast>(high_resolution_clock::now() - t); + + EXPECT_TRUE(optimal_t < 0.4 * naive_t); +} + +TEST(TestLabo1, Supplementaires) +{ + // TODO remplacez le code avec vos propres tests ici + EXPECT_TRUE(false); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + const int ret = RUN_ALL_TESTS(); + + return ret; +}