TP3: Transformations Affines Récursives

Le fichier à remplir est ici.

Nous allons calculer un ensemble de points du plan qui sont les termes successifs d'une suite définie au moyen de transformations affines dans \(\mathbb{R}^2\). L'affichage de ces points forme un dessin fractal.

Dans ce TP, on fixe un point \(O\) du plan, l'origine, et on définit des matrice 2x2 et des vecteurs comme suit.

\begin{align*} M &= \left[\begin{matrix} a & b \\ c & d \end{matrix} \right] & \overrightarrow{OP} &= \left[\begin{matrix} x \\ y \end{matrix} \right] \end{align*}

Une application linéaire \(f\) est une fonction qui à un vecteur associe un vecteur. Elle est définie par une matrice \(M\) (la matrice associée à \(f\)) de la manière suivante : \(f = \overrightarrow{OP} \mapsto M \times \overrightarrow{OP}\).

Une transformation affine du plan est également une fonction qui associe un vecteur à un vecteur, \(\overrightarrow{OP} \mapsto \overrightarrow{OP_1}\), définie par \(\overrightarrow{OP_1} = f(\overrightarrow{OP}) + \vec V\), où \(f\) une application linéaire et \(\vec V\) un vecteur fixé.

Par conséquent, on peut définir une transformation affine par la combinaison d'une matrice \(M\), définissant l'application linéaire \(f\), et un vecteur \(\vec V\).

1 Choix de représentation

Choisir et définir un type matrix pour les matrices 2x2 et un type vector pour les vecteurs de taille 2. Définir deux fonctions construisant respectivement une matrice et un vecteur.

val mkm : float -> float -> float -> float -> matrix = <fun>
val mkv : float -> float -> vector = <fun>

Définir une fonction transformant un vecteur en une liste à deux éléments.

val vtol : vector -> float list = <fun>

Définir une fonction transformant une matrice en une liste de deux listes à deux éléments, chacune des liste étant une ligne.

val mtol : matrix -> float list list = <fun>

2 Définition des données

Définir les 4 matrices et les 4 vecteurs suivants.

(* Evaluate me *)
let m1 = mkm 0.0 0.0 0.172 0.496
let m2 = mkm 0.076 0.3122 0.257 0.204
let m3 = mkm 0.821 (-0.028) 0.030 0.845
let m4 = mkm 0.024 (-0.356) 0.323 0.074
let v1 = mkv 0.496 (-0.091)
let v2 = mkv 0.494 0.133
let v3 = mkv 0.088 0.176
let v4 = mkv 0.470 0.260

3 La construction des transformations affines

  1. Définir une fonction mt qui réalise le produit d'une matrice \(2 \times 2\) par un vecteur.

    val mt : matrix -> vector -> vector = <fun>
    
  2. Définir une fonction sv qui réalise la somme de 2 vecteurs.

    val sv : vector -> vector -> vector = <fun>
    
  3. Définir une fonction genapplin qui prend en argument une matrice et retourne l'application linéaire associée.

    val genapplin : matrix -> vector -> vector = <fun>
    
  4. Définir une fonction gentraffine qui prend en argument une matrice et un vecteur, et qui retourne la transformation affine associée.

    val gentraffine : matrix -> vector -> vector -> vector = <fun>
    
  5. Définir les quatre transformations affines associées respectivement à \(m_1\) et \(v_1\), \(m_2\) et \(v_2\), \(m_3\) et \(v_3\), et \(m_4\) et \(v_4\), et les stocker dans le quadruplet les4tr. Rappel : ces matrices et vecteurs sont déjà définis dans le fichier.

    val les4tr :
      (vector -> vector) * (vector -> vector) * (vector -> vector) *
      (vector -> vector) = (<fun>, <fun>, <fun>, <fun>)
    

4 Le choix d'une transformation parmi les 4

  1. Définir une fonction elemrang qui retourne un élément choisi au hasard (compris entre \(1\) et \(4\)) d'un quadruplet donné. Utiliser la fonction de la bibliothèque Random.int : int -> int, qui est telle que Random.int n retourne un nombre au hasard dans l'intervalle d'entiers \([0..n-1]\).

    val elemrang : 'a * 'a * 'a * 'a -> 'a = <fun>
    
  2. Définir une fonction traff qui délivre au hasard l'une des 4 transformations affines, en utilisant elemrang.

    val traff : unit -> vector -> vector = <fun>
    

5 La suite des points transformés

Nous nous intéressons maintenant à une suite de points du plan définie par :

\begin{align*} P_0 & = (0.5, 0.0)\\ P_i & = T(P_{i-1}) \end{align*}

où \(T\) désigne l'une "au hasard" des 4 transformations affines.

Définir la fonction suite qui, étant donné une fonction f et un entier n, calcule les n termes de la suite et appelle la fonction f sur chaque terme. La fonction f est utilisée pour l'affichage (son type est vector -> unit), et la suite de points calculée avec la fonction traff.

val suite : (vector -> unit) -> int -> unit = <fun>

6 L'affichage de la suite des points

Pour démarrer une fenêtre graphique, exécuter ceci:

(* Copiez la ligne suivante (avec le #) dans le toplevel (fenêtre du bas) et
   tapez Entrée
#load "graphics.cma";;
*)
let res = Graphics.open_graph ""

Attention, si vous fermez directement la fenêtre, cela tue le toplevel. Il vaut mieux utiliser la fonction close_graph.

let res = Graphics.close_graph ()

Documentation du module Graphics: http://caml.inria.fr/pub/docs/manual-ocaml/libgraph.html
http://caml.inria.fr/pub/docs/manual-ocaml/libref/Graphics.html

Pour afficher les points, définissez une fonction run qui prend en argument le nombre de points à afficher :

  • la fonction ouvre une fenêtre graphique;
  • elle calcule et affiche le nombre de points demandés;
  • elle attend que l'on tape une touche du clavier (en utilisant la fonction wait_next_event de Graphics);
  • elle ferme la fenêtre et quitte.
val run : int -> unit = <fun>