Rendu web de formules mathématiques


1 décembre 2007
Cette page date un peu et le paquet texgd n'étant pas utilisable sur mon serveur actuel, j'ai modifié le processus de transformation de formules LaTeX en PNG que j'ai documenté sur une nouvelle page.

Introduction

J'utilise le langage TeX pour afficher toutes les formules mathématiques sur ce site et cette page explique la mise en oeuvre de cette fonctionnalité.

Tex2Png : texgd + PHP

Le coeur de la solution c'est l'exécutable texgd, une application qui s'installe sur le serveur envisagé. Utilisant Ubuntu je n'ai eu besoin que d'exécuter la commande suivante pour disposer de cette application sur ma machine :

apt-get install texgd

La doc ("man texgd") est courte mais suffit pour utiliser cette application, plutôt spartiate dans son genre. En gros : on exporte des variables d'environnement Bash pour passer des paramètres à l'application, que l'on lance directement, sans arguments. Elle génère une image selon les variables d'environnement exportées qui sont la formule, le chemin du fichier image généré etc.

La passerelle web

Il ne reste maintenant qu'à décrire la partie PHP : c'est un script qui gère un dossier de cache d'images pour appeler tex2gd uniquement quand il y en a besoin et économiser beaucoup de temps machine qu'un serveur web doit économiser par-dessus tout :

<?
    //Ce script nécessite d'installer le package texgd et la présence de plusieurs répertoires ayant les privilèges d'écriture règlés pour le serveur web (l'utilisateur sous lequel tourne ce script).
    $Formula = str_replace("\\\\", "\\", rawurldecode($_SERVER["QUERY_STRING"]));
    $PicName = md5($Formula).".png";
    $CacheDir = "mathcache/";
    $PicFilePath = "http://www.flubb.net/"."$CacheDir$PicName";
    $TmpPath = $CacheDir."tmp/";
    //On ne crée l'image que si elle n'existe pas déjà dans le cache
    if (!file_exists($PicFilePath))     {
        $TexGDCommand = "
            export texgd_src='$Formula'
            export texgd_tmpdir=$TmpPath
            export texgd_fontdir=".$CacheDir."mathfonts
            export texgd_outfile=$CacheDir$PicName
            export texgd_texheader=".$CacheDir."header.tex
            export texgd_style='\$\$'
            export texgd_density=10
            export texgd_compressratio='3'
            texgd";
        $Ret = exec($TexGDCommand, $Output);
    }
    //On envoie un tag HTML contenant la référence de l'image générée.
    echo "<img style='vertical-align:middle;' src='$PicFilePath' border='0' title='$Formula'/>";
?>

Ce script est une page à part entière. L'intérêt ? Et bien si on écrit l'URL suivante dans la barre d'adresse de son navigateur :

http://www.flubb.net/tex2png.php?I_2=\frac{x-a}{2}f^{\prime\prime}(a)+\int_{a}^x\frac{x-t}{2}f^{(3)}(t)dt

et bien vous verrez l'image suivante apparaître dans votre navigateur :

Exemples

En cours...

Liens externes

J'aurais pu utiliser d'autres applications/scripts pour faire des conversions de formules TeX en PNG, comme ceux-ci : Charles' Latex Page : une page intéressante qui propose des infos sur TeX mais également des schémas de conversion. TeX2png.php : un code qui fait la conversion TeX en PNG mais en partant de bien plus bas, et plus intéressant, que texgd. Là, on appelle TeX directement. Note : c'est issu du projet PHPWiki. Des vidéos de Knuth : ça n'a presque rien à voir avec ce qui précède, si ce n'est qu'il cause de TeX :)

Formule de Taylor


4 septembre 2007

Présentation

développer parce que sinon ça fait trop étudiant de première année...

J'ai eu envie de démontrer la formule de Taylor ci-dessous pour deux raisons : d'abord parce que c'est une étape capitale de l'histoire des mathématiques et qu'aucun des profs ou des bouquins de ma jeunesse ne me l'avait exposée proprement et ensuite parce qu'au moins comme ça je l'aurai quelque part !

Rappel : la formule de Taylor sert à réaliser une approximation d'une fonction f au point x ne connaissant que sa valeur et celles de ses dérivées successives au point a, considéré comme voisin de x. Sa version infinie s'écrit :

ou encore, de manière plus concise :

L'intégrale qui subsiste à la fin de l'expression est le reste de l'approximation de Taylor. Le principe étant d'aller aussi loin que possible dans les n, on l'ignore couramment (ou on ajoute des points de suspension) quand on écrit l'approximation d'une fonction connue, par exemple :

La démonstration

Tout commence avec l'égalité fondamentale suivante :

Ceci est connu de tous avant le bac. Maintenant si on change l'ordre des termes on trouve :

�a n'est pas mieux ? Si, car on va ré-écrire le dernier terme avec une intégration par parties :

or le terme nous embête car il est évalué en x et pas en a (rappel : je veux que tout ce qui se situe à droite du signe égal ne soit évalué qu'en a !). On ré-écrit donc ce terme comme suit :

que l'on peut réintégrer dans {3} :

On peut continue d'appliquer le même genre d'intégration par partie pour développer le terme intégral et ainsi affiner notre approximation :

or le terme entre crochets s'annule pour t=x et on obtient :

et en intégrant cette expression ainsi que {4} dans {3} on aboutit à la formule de Taylor d'ordre 3 :

� ce moment, on imagine que cette expression, par intégrations par parties successives, va aboutir à {1}, chose que l'on vérifie par induction.

Applications

Un programme ? L'évaluation d'une série de Taylor peut être utile, si on ne retient que quelques termes du calculs pour aller plus vite, et si on fait bien attention à ne pas perdre de précision avec des multiplications ou divisions répétitives inutiles (Faire un lien). Par exemple, l'évaluation d'un sinus pour des valeurs inférieures à PI/N se fera plus rapidement et sans perte significative de précision avec le code suivant : Pour le domaine [], une tangente sera calculée avec le bout de code ci-dessous plus rapidement qu'avec la fonction tan() de la libc : En gros en tronquant l'approximation, en oubliant le reste, on a quelque chose d'utilisable.

Séries utiles

Références

La démonstration version Wikipedia

Librairie de Math


19 août 2007

Librairie de math

La librairie que je suis en train de concevoir pour exécuter, entre autres choses, des calculs sur des polynômes, des vecteurs et des matrices.

#include <stdarg.h>
#include <math.h>

using namespace std;

class   QiXMath : public std::exception
{
    public:
        QiXMath(char* msg)      { _whatMsg = msg; }
        virtual const char* what() const throw()    {   return _whatMsg;    }
    protected:
        char*   _whatMsg;
};


//Pour représenter des fractions, ce qui est très utile pour les polynôme quand on veut des calculs exacts.
//Note : pour simplifier des nombres décimaux qui ont des séquences répétitives voir http://en.wikipedia.org/wiki/QiFraction_(mathematics)
//TOCHECK : l'opérateur /(int) ne doit pas être nécéssaire puisque j'ai un constructeur qui a un int en entrée, pourquoi dois-je en avoir un ???
template <class T>
class   QiFraction
{
    public:
        //Faire un constructeur avec un seul paramètre : le numérateur.
        QiFraction(T n, T d)        {   _n = n; _d = d; }
        QiFraction(T n=0)       {   _n = n; _d = 1; }
        QiFraction(float n) {   _n = (T) n; _d = 1; }
        QiFraction(int n)   {   _n = (T) n; _d = 1; }
        void    dump()      const
        {
            if (_n == 0 || _d == 1)     cout << _n;
            else    {
                if (_n == _d)       cout << "1";
                else    cout << _n << "/" << _d;
            }
        }
        QiFraction<T>&  operator+=(const QiFraction<T>& a)
        {
            T   P = pgcd(_d, a._d);
            T   NewD = (_d * a._d) / P;
            _n = _n * (NewD / _d) + a._n * (NewD / a._d);
            _d = NewD;
            return *this;
        }
        QiFraction<T>&  operator-=(const QiFraction<T>& a)
        {
            T   P = pgcd(_d, a._d);
            T   NewD = (_d * a._d) / P;
            _n = _n * (NewD / _d) - a._n * (NewD / a._d);
            _d = NewD;
            return *this;
        }
        QiFraction<T>&  operator*=(const QiFraction<T>& a)
        {
            _n *= a._n;
            _d *= a._d;
            //On simplifie la fraction
            T   Common = pgcd(_n, _d);
            _n /= Common;
            _d /= Common;
            return *this;
        }
        QiFraction<T>&  operator/=(const QiFraction<T>& a)
        {
            _n *= a._d;
            _d *= a._n;
            //On simplifie la fraction
            T   Common = pgcd(_n, _d);
            _n /= Common;
            _d /= Common;
            return *this;
        }
        QiFraction<T>   operator+(const QiFraction<T>& a)       const
        {
            QiFraction  Result(_n, _d);
            return Result += a;
        }
        QiFraction<T>   operator-(const QiFraction<T>& a)       const
        {
            QiFraction  Result(_n, _d);
            return Result -= a;
        }
        QiFraction<T>   operator*(const QiFraction<T>& a)       const
        {
            QiFraction  Result(_n, _d);
            return Result *= a;
        }
        QiFraction<T>   operator/(const QiFraction<T>& a)       const
        {
            QiFraction  Result(_n, _d);
            return Result /= a;
        }
        QiFraction<T>   operator/(T a)      const
        {
            QiFraction<T>   Result(_n, _d);
            return Result /= QiFraction<T>(a);
        }
        QiFraction<T>   operator/(int a)        const
        {
            QiFraction<T>   Result(_n, _d);
            return Result /= QiFraction<T>(a);
        }
        bool    operator>=(float t) const       {   return (float)_n/(float)_d > t;     }
        bool    operator==(float t) const       {   return (float)_n/(float)_d == t;    }
        operator float()    const   {   return (float)_n/(float)_d; }
        QiFraction<T>&  inverse()
        {
            T   Tmp = _n;
            _n = _d;
            _d = _n;
            return *this;
        }
        T   pgcd(T u, T v/*, int verbose=false*/)
        {
            T   M = 1;
            if (u<0)        u = (T)0-u;
            if (v<0)        v = (T)0-v;
            while (u && v)      {
//      if (verbose)        printf("pgcd(%d, %d)\n", u, v);
                if (!(u&1) && !(v&1))       { M <<= 1; u>>=1, v>>=1; }
                else    if (!(u&1) && v&1)      u>>=1;
                else    if (u&1 && !(v&1))      v>>=1;
                else    {       //Les deux nombres sont impairs
                    if (u>v)    u=(u-v)>>1;
                    else        v=(v-u)>>1;
                }
            }
            return M * (u ? u : v);
        }
//Note : ppcm(a,b) = (a*b)/pgcd(a,b)

    T   _n, _d;     //Numérateur et dénominateur
};


//###TODO (25%) : Exceptions
//###TODO : cacher l'évaluation du degré d'un polynôme
//###TODO : faire la factorisation simple d'un polynôme
//###TODO : faire des opérateurs pour QiMatrix et QiPolynomial
//###TODO : faire des opérateurs pour QiVector
template <class T>
class   QiPolynomial
{
    public:
        QiPolynomial(int rank)
        {
            _comps = NULL;
            _init(rank);
        }
        ~QiPolynomial()
        {
            DELETEARRAY(_comps);
        }
        QiPolynomial(const QiPolynomial<T>& original)
        {
            if (this != &original)      {
                _init(original.rank());
                int i;
                for(i=0; i<=rank(); i++)        _comps[i] = original.get(i);
            }
        }
    protected:
        void    _init(int rank)
        {
            if (rank<0)     return;
            _rank = rank;
            _comps = new T[_rank+1];
        }
        void    _expand(int nbWantedSlots)
        {
            int NewSize = nbWantedSlots;
            _comps = new T[NewSize];
            if (!_comps)        throw new QiXMath("QiPolynomial::_expand : NotImplemented");
//          memcpy();
        }
    public:
        //
        void    null()
        {
            int i;
            for(i=0; i<=rank(); i++)        _comps[i] = 0.0f;
        }
        void    primitive()
        {
            int Degree = degree();
            if (rank() < Degree+1)      _expand(Degree+1);
            int i, MaxIndex = degree();
            for(i=MaxIndex; i>=0; i--)      _comps[i+1] = _comps[i]/(i+1);
            _comps[0] = 0.0f;
        }
        void    add(const QiPolynomial<T>& op)
        {
            int i;
            int MaxIndex = op.rank();
            if (rank() < MaxIndex)      MaxIndex = rank();
            for(i=0; i<=MaxIndex; i++)          set(i, get(i) + op.get(i));
        }
        QiPolynomial<T>&    operator *= (const QiPolynomial<T>& a)
        {
            multiply(a);
            return *this;
        }
        bool    multiply(const QiPolynomial<T>& op)
        {
            if (degree() + op.degree() > rank())        return false;
            QiPolynomial    Temp(degree() + op.degree());
            int MaxIt = op.degree(), i, j;
            int CurrentDegree = degree();
            Temp.null();
            for(i=0; i<=MaxIt; i++)     {
                for(j=0; j<=CurrentDegree; j++)     {
                    T   CurrentValue = Temp.get(i+j);
                    T   NewInc = op.get(i) * get(j);
                    Temp.set(i+j, CurrentValue + NewInc);
                }
            }
            set(Temp);
            return true;
        }
        void    multiply(T coeff)
        {
            int i;
            int MaxIndex = degree();
            for(i=0; i<=MaxIndex; i++)      set(i, get(i)*coeff);
        }
        QiPolynomial<T>&    operator /= (const T& a)
        {
            int i;
            int MaxIndex = degree();
            for(i=0; i<=MaxIndex; i++)      {
                set(i, get(i) / a);
            }
            return *this;
        }
        void    set(const QiPolynomial& src)
        {
            int i;
            null();
            int MaxIndex = rank();
            if (MaxIndex>src.rank())        MaxIndex = src.rank();
            for(i=0; i<=MaxIndex; i++)      set(i, src.get(i));
//          _rank = src.rank();
        }
        void    setRaw(int maxDegree, ...)
        {
            if (maxDegree > rank())     return;
            va_list args;
            va_start(args, maxDegree);
            int i, j;
            for(i=maxDegree; i>=0; i--)     {
                float   Value = (float) va_arg(args, double);
                _comps[i] = Value;
            }
            va_end(args);
        }
        //On remplace la variable x du polynôme par un autre polynôme (x par x+1 par exemple).
        //On substitue donc Q(x) à x dans P(x), ce qui donne P(Q(x)), un autre polynôme en x.
        //La méthode :
        void    substitute(const QiPolynomial& newVar)
        {
            QiPolynomial    Value(newVar.degree() + degree()), Total(newVar.degree() + degree());
            QiPolynomial    Param(newVar.degree() + degree());
            int i;
            Param.null();
            Param.set(0, 1.0f);
            for(i=0; i<=degree(); i++)      {
                Value.set(Param);
                Value.multiply(get(i));
                Total.add(Value);
                Param.multiply(newVar);
            }
            set(Total);
        }
        T   get(int index)  const
        {
            if (index<0 || index>rank()+1)      throw new QiXMath("QiPolynomial::get out of bound");
            return _comps[index];
        }
        void    set(int index, T value)
        {
            if (index<0 || index>rank()+1)      throw new QiXMath("QiPolynomial::set out of bound");
            _comps[index] = value;
        }
        float   evaluate(T x)   const
        {
            int MaxIndex = degree();
            float   Current = 1.0f, Total = 0.0f;
            int i;
            for(i=0; i<=MaxIndex; i++)      {
                Total += Current * _comps[i];
                Current *= x;
            }
            return Total;
        }
        int     degree()    const       //Renvoie le degré du polynôme courant : l'indice du premier coefficient non-nul en partant des puissances élevées.
        {
            int i = rank();
            while (get(i) == 0.0f && i>0)       i--;
            return i;
        }
        void    dump()  const
        {
            int i, Degree = degree();
            for(i=Degree; i>=0; i--)        {
                if (i<Degree && get(i)>=0.0f)       printf("+");
                printf("%.10f", (float) get(i));
                if (!i)     continue;
                if (i==1)   printf("X");
                if (i>1)    printf("X^%d", i);
            }
            printf("\n");
        }
        void    dumpObjects()   const
        {
            int i, Degree = degree();
            for(i=Degree; i>=0; i--)        {
                T   CurValue = get(i);
                if (i<Degree && CurValue>=0.0f)     printf("+");
                if (CurValue != 0.0f)       {
                    get(i).dump();
                    if (!i)     continue;
                    if (i==1)   printf("X");
                    if (i>1)    printf("X^%d", i);
                }
            }
            printf("\n");
        }
        int     rank()  const   {   return _rank;   }
    protected:
        T*      _comps;
        int     _rank;
};

template <class T>
class   QiVector
{
    public:
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        class   ExQiVector : public std::exception
        {
            public:
                ExQiVector(char*)       {   }
        };
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        QiVector(int rank)      {   _comps = new T[rank];   _rank = rank;}
        ~QiVector()     {   DELETEARRAY(_comps) }
        void    null()  {
            int i;
            for(i=0; i<rank(); i++)     _comps[i] = 0.0f;
        }
        //On crée un vecteur de base
        void    base(int index)
        {
            if (index<0 || index>=rank())       return;
            null();
            set(index, 1.0f);
        }
        //###TODO : faire des exceptions pour les out of bounds.
        bool    set(int index, const T& value)  {
            if (index<0 || index >= rank())     return false;
            _comps[index] = value;
            return true;
        }
        bool    set(QiVector<T>& newValue)
        {
            if (rank() != newValue.rank())      return false;
            int i;
            for(i=0; i<rank(); i++)     _comps[i] = newValue.get(i);
            return true;
        }
        bool    add(int index, const T& value)
        {
            if (index<0 || index>=rank())       return false;
            return set(index, get(index) + value);
        }
        T   get(int index)      const
        {
            return _comps[index];
        }
        QiVector<T>& operator=(const QiVector<T>& original)
        {
            //###TODO : si les rangs sont différents on doit changer le rang de l'objet courant
            if (this != &original && rank() == original.rank())     {
                int i;
                for(i=0; i<rank(); i++)     _comps[i] = original.get(i);
            }
            return *this;
        }
        int     rank()  const   {   return _rank;   }
        void    dump()  const
        {
            int i;
            printf("(");
            for(i=0; i<rank(); i++)     {
                if(i)   printf(", ");
                printf("%f", _comps[i]);
            }
            printf(")\n");
        }
        void    dumpObjects()   const
        {
            int i;
            printf("(");
            for(i=0; i<rank(); i++)     {
                if(i)   printf(", ");
                _comps[i].dump();
            }
            printf(")\n");
        }
    protected:
        T*  _comps;
        int _rank;
};

//Matrice carrée constituée de vecteurs QiVector
template <class T>
class   QiMatrix
{
    public:
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        class   ExQiMatrix : public std::exception
        {
            public:
                ExQiMatrix(char*)       {   }
        };
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        QiMatrix(int rank)      {   _init(rank);    }
    protected:
        bool _init(int rank)
        {
            _comps = new QiVector<T>*[rank];
            int i;
            for(i=0; i<rank; i++)       _comps[i] = new QiVector<T>(rank);
            _rank = rank;
            return true;
        }
        bool _release()
        {
            int i;
            for(i=0; i<rank(); i++)         {
                DELETESINGLE(_comps[i]);
            }
            DELETEARRAY(_comps);
        }
        void    _count()    const
        {
            _nbUps = 0, _nbLows = 0, _nbDiags = 0;
            int i, j;
            for(i=0; i<rank(); i++)     {
                for(j=0; j<rank(); j++)     {
                    float   Value = get(i, j);
                    if (i>j && Value)       _nbLows++;
                    if (i<j && Value)       _nbUps++;
                    if (i==j && Value)      _nbDiags++;
                }
            }
            //
            _nbItemsInHalf = _rank*(_rank-1)/2 + _rank;
        }
    public:
        ~QiMatrix()     {   _release(); }

        void    null()      {   int i;  for(i=0; i<rank(); i++)     _comps[i]->null();  }
        void identity()
        {
            null();
            int i;
            for(i=0; i<rank(); i++)     _comps[i]->set(i, 1.0f);
        }
        T   get(int i, int j)       const
        {
            if (i<0 || j<0)     return 0.0f;
            if (i>=rank() || j>=rank())     return 0.0f;
            return _comps[i]->get(j);
        }
        QiVector<T>& get(int index) const
        {
            if (index<0 || index>=rank())       return *_comps[0];
            return *_comps[index];
        }
        bool    set(int i, int j, T value)
        {
            if (j<0 || j>=rank())   return false;
            _comps[i]->set(j, value);
            return true;
        }
        bool    setRaw(int setRank, ...)        {
            if (setRank != rank())  return false;
            va_list args;
            va_start(args, setRank);
            int i, j;
            for(i=0; i<rank(); i++)     {
                for(j=0; j<rank(); j++)     {
                    if (!_comps[i]->set(j, (float) va_arg(args, double)))       {
                        va_end(args);
                        return false;
                    }
                }
            }
            va_end(args);
            return true;
        }
        bool    setRow(int index, QiVector<T>& row)
        {
            if (rank() != row.rank())       return false;
            _comps[index]->set(row);
            return true;
        }
        //Multiplication matrice-matrice inplace
        QiMatrix<T>&    operator *= (const QiMatrix<T>& op)
        {
            multiply(op);
            return *this;
        }
        bool    multiply(const QiMatrix<T>& op)
        {
            if (rank() != op.rank())        return false;
            QiVector<T> Temp(rank());
            int i, j, k;
            for(i=0; i<rank(); i++)     {
                for(Temp.null(), j=0; j<rank(); j++)        {
                    for(k=0; k<rank(); k++)     {
                        Temp.add(j, get(i, k) * op.get(k, j));
                    }
                }
                setRow(i, Temp);
            }
            return true;
        }
        //
        QiVector<T> operator * (const QiVector<T>& op)      const
        {
            QiVector<T>     Result(op.rank());
            multiply(op, Result);
            return Result;
        }
        void    multiply(const QiVector<T>& op, QiVector<T>& result)    const
        {
            int i, j;
            for(i=0; i<rank(); i++)     {
                T   Total = 0.0f;
                for(j=0; j<rank(); j++)     {
                    Total += get(i, j) * op.get(j);
                }
                result.set(i, Total);
            }
        }
        //Copy constructor
        QiMatrix(const QiMatrix<T>& original)
        {
            _init(original.rank());
            int i;
            for(i=0; i<rank(); i++)     *_comps[i] = original.get(i);
        }
        bool    isLower()   const
        {
            _count();
            return _nbLows + _nbDiags == _nbItemsInHalf;
        }
        bool    isDiagonal()    const
        {
            _count();
            return _nbDiags == rank();
        }
        //Inverse la matrice courante "inplace"
        void    inverse()
        {
            if (isLower())      {
                //Cas pour les matrices triangulaires inférieures
                QiVector<T> Temp(rank());
                int i, j, k;
                T   Total;
                for(i=0; i<rank(); i++)     {
                    Temp.null();
                    for(j=0; j<=i; j++)     {
                        Total = 0.0f;
                        for(k=j; k<i; k++)      {
                            T   NormalIK = get(i, k);
                            T   InvKJ = get(k, j);
                            Total += NormalIK * InvKJ;
                        }
                        T   DeltaIJ = i == j ? 1.0f : 0.0f;
                        T   MainCoeff = get(i, i);
                        T   InverseCoeff = (DeltaIJ - Total) / MainCoeff;
                        Temp.set(j, InverseCoeff);
                    }
                    setRow(i, Temp);
                }
            }   else    if(isDiagonal())        {
                int i;
                for(i=0; i<rank(); i++)     set(i, i, 1.0f/get(i, i));
            }   else    throw new QiXMath("QiMatrix::inverse : not yet implemented.");
        }

        void    dump()  const
        {
            int i;
            for(i=0; i<rank(); i++)     _comps[i]->dump();
        }
        void    dumpObjects()   const
        {
            int i;
            for(i=0; i<rank(); i++)     _comps[i]->dumpObjects();
        }
        int     rank()  const   {   return _rank;   }
        //
        bool    isUpper()   const;
        bool    isInversible()  const;
    protected:
        QiVector<T>**   _comps;
        int _rank;
        //
        mutable     int _nbUps, _nbLows, _nbDiags, _nbItemsInHalf;
};

Bien se souvenir qu'ici je cherche plus à être exact que rapide, c'est sûr que pour un moteur 3D j'utiliserai les classes QiMatrix et QiVector avec beaucoup de pincettes !!!

Marching methods


7 avril 2007

Poursuite du livre de Sethian

Deux méthodes pour suivre un front (d'onde, de fluide etc), connaissant sa vitesse normale, F, de propagation : l'une basée sur la valeur initiale (valable quelque soit F) et une autre sur les conditions aux limites (valable pour F>0).

Poursuite du livre de Sethian

On introduit plus formellement l'expression du problème et ses équations principales. On parle alors de métrique, de variation des oscillations en intégrant sur l'ensemble du front pour un t donné, de courbature etc. On aborde également la notion "d'entropie", que Sethian utilise et qui a du sens, même si ça n'est pas le même qu'en thermo ou en physique stat. On voit pour la première fois les effets d'un avancement du temps et les éventuelles pertes d'information que cela entraîne : comment intégrer ça dans la résolution d'une équation ? Une équation importante est la (2.16) qu'il faut redémontrer facilement.

Chapitre 3

Honnêtement je ne vois pas la pertinence de ce chapitre, non pas qu'il soit inutile, mais je ne le suis pas. C'est à relire car il est sûrement très intéressant pour la suite. Pour l'instant j'hypothèque...

Géométrie en 4 dimensions


7 avril 2007

Tout part d'une constatation évidente sur la construction d'un cube à partir de deux simples points 1D :

(-1), (1)

Pour passer à un carré (2D) et enfin au cube (3D) il suffit de réaliser deux produits tensoriel avec le vecteur (-1, 1) :

(-1, -1) (1, -1)
(-1, 1) (1, 1)
Un carré
(-1, -1, -1) (1, -1, -1)
(-1, 1, -1) (1, 1, -1)
(-1, -1, 1) (1, -1, 1)
(-1, 1, 1) (1, 1, 1)
Un cube

Et en suivant la même logique on arrive à un objet à 16 points 4D, un hypercube :

(-1, -1, -1, -1) (1, -1, -1, -1)
(-1, 1, -1, -1) (1, 1, -1, -1)
(-1, -1, 1, -1) (1, -1, 1, -1)
(-1, 1, 1, -1) (1, 1, 1, -1)
(-1, -1, -1, 1) (1, -1, -1, 1)
(-1, 1, -1, 1) (1, 1, -1, 1)
(-1, -1, 1, 1) (1, -1, 1, 1)
(-1, 1, 1, 1) (1, 1, 1, 1)

Le but est maintenant de trouver comment projeter ces 16 points 4D en 3 dimensions pour le rendre sur un simple écran.

Lien 1 Lien 2 Lien 3
Accueil1 2 3