Cycle moteur de rendu 2D TypeScript

Sous-titre de mon tutoriel

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Barista - Les bases

On se retrouve aujourd’hui pour la grande première de Barista, le tout premier cycle du grand programme de formation. Cette série de tutoriels ne nécessite pas forcément de gros prérequis, elle demande cependant une certaine ténacité, car la difficulté peut augmenter très vite. C’est d’ailleurs pour cela que je lui donne la note de 3 cafés! Pour celles et ceux qui dormaient au fond de la classe, voici un bref récapitulatif des faits:

video à placer Barista version 2020

La première version de Barista, précédemment nommée Tomahawk, était écrite en Javascript ES5, cela commence à dater! Cette version apportera son lot de nouveautés, comme l’utilisation de Typescript, Typedoc, Jasmine et Webpack. Commençons déjà par l’installation de notre projet , j’ai détaillé toute la procédure en vidéo juste ici:

video à placer Installation du projet

I-A. Dessiner avec la balise canvas

Nous allons commencer par créer un fichier de base pour notre application sur la base du modèle suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Barista</title>
    <script src="./dist/main.js"></script>
</head>
<body>
    <canvas id="barista"></canvas>
</body>
</html>
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
/* Point d'entrée de l'application */
function init(){
    //code de notre Application
}
/* 
* Quand toutes les données sont chargées (DOM, images, sons etc...)
* On démarre l'application par la fonction init
*/
window.addEventListener("load", init);

Le fichier index.html est un fichier HTML5 simple, les seuls éléments notables sont :

  • l’inclusion d’un fichier Javascript ./dist.main.js.
  • la présence d’une balise dont l’id est « barista ».

Nous allons pouvoir passer à la suite, çàd créer un canvas et l’utiliser.

I-B. Créer un canvas

Créer un canvas peut se faire de deux façons :

  • soit de la manière la plus simple qui soit, c’est-à-dire, en ajoutant une balise au sein du DOM (dans le code HTML) ;
  • soit en la créant par programmation et en l’ajoutant manuellement au DOM de la façon suivante :
 
Sélectionnez
1.
2.
const canvas = document.createElement("canvas");
document.querySelector("body").appendChild(canvas);

Dans notre exemple, nous choisirons la première méthode, c’est-à-dire ajouter une balise au code HTML. Notre fichier index.html en contient déjà une, nous allons donc créer une fonction pour la retrouver.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
/*
* Retourne une référence à l'objet canvas crée à l'aide de la balise 
* placée dans le code html
*/
function getCanvas(){
    return document.querySelector("canvas"); 
}

À l’aide de cette méthode, nous avons accès à l’objet canvas, maintenant il nous faut pouvoir dessiner à l’intérieur. Pour ce faire nous allons d’abord devoir récupérer le contexte de l’objet canvas, pour de plus amples informations sur ce qu’est un contexte je vous invite à vous rendre sur le site du W3C. Le contexte de l’objet canvas se récupère à l’aide de la méthode getContext() la façon suivante:

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
/*
* Retourne le contexte d'éxécution 2d du canvas
*/
function getContext(){
    return getCanvas().getContext("2d");
}
/*
* Retourne une référence à l'objet canvas crée à l'aide de la balise 
* placée dans le code html
*/
function getCanvas(){
    return document.querySelector("canvas"); 
}

I-C. Dessiner des primitives

video à placer Comment dessiner des primitives avec canvas ?

HTML5 embarque toute une API dédiée au dessin, ce qui permet aux développeurs de créer du contenu 2d sans avoir systématiquement recours à des images. À l’aide de cette API, on peut dessiner n’importe quelle forme géométrique, voire des dessins plus complexes, la seule vraie limite est votre imagination.

I-C-1. Dessiner une ligne

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
/*
* Retourne le contexte d'éxécution 2d du canvas
*/
function getContext(){
    return getCanvas().getContext("2d");
}
/*
* Retourne une référence à l'objet canvas crée à l'aide de la balise 
* placée dans le code html
*/
function getCanvas(){
    return document.querySelector("canvas"); 
}

/* Point d'entrée de l'application */
function init(){
    let canvas = getCanvas();
    let context  = getContext(); 

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // crée un nouveau path 
    context.beginPath(); 

    // on définit l'épaisseur de la ligne (unité en px)
    context.lineWidth = 5; 

    // on bouge vers les coordonnées x = 10 et y = 10 (sans rien dessiner)
    context.moveTo(10,10); 

    // on dessine une ligne depuis notre point de départ vers le point d'arrivée (x=10,y100)
    context.lineTo(10,100); 

    // on définit la couleur de la ligne, ici c'est du vert.
    context.strokeStyle = "#00ff00"

    // et enfin on appelle la méthode stroke, qui va se charger d'éxécuter l'ensemble 
    // des commandes que nous avons utilisé depuis que l'on invoqué beinPath()
    // si l'on commente l'appel à cette méthode, rien ne se produit. 
    // L'appel à stroke permet de fermer le chemin crée par beginPath().
    context.stroke();
}
/* 
* Quand toutes les données sont chargées (DOM, images, sons etc...)
* On démarre l'application par la fonction init
*/
window.addEventListener("load", init);

Avant toute opération de dessin en HTML5, il nous faudra commencer par un beginPath() qui permet, comme son nom l’indique, de commencer un « chemin », comprendre par là que l’on initialise un nouveau cycle de dessin, un peu comme si l’on prenait une nouvelle feuille vierge pour dessiner. Faisons le point de ce que nous avons actuellement sur notre canvas, une ligne dont les propriétés sont :

  • Une épaisseur de 5px définie par la propriété lineWidth = 5 ;
  • Une couleur définie par la propriété strokeStyle = ‘#003300’ ;
  • Un point de départ situé à x = 10px et y = 10px que nous avons défini avec l’appel à la fonction moveTo;
  • Un point d’arrivée situé à x = 10px et y = 100px que nous avons relié au point de départ en faisant appel à la fonction lineTo, qui relie le dernier point dessiné au point dont les coordonnées sont passées en paramètres.

Vous pouvez remarquer que la dernière ligne de notre code se termine par context.stroke(), cette méthode permet de dessiner l’ensemble du jeu d’instructions définis entre l’appel à context,beginPath() et context.stroke(), si vous commentez l’appel à cette méthode, rien ne sera dessiné.

Notez également que context.stroke() n’exécute que les jeux d’instructions relatifs aux lignes et pas aux formes pleines, ces dernières sont gérées de manière différente, ce qui nous amène à la suite, dessiner des primitives « pleines », en commençant par le cercle.

I-C-2. Dessiner un cercle

Si je souhaite dessiner un cercle, je peux utiliser la méthode suivante :

 
Sélectionnez
1.
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);

Où x et y représentent les coordonnées du centre de mon arc, radius le rayon ( en pixels ) de mon arc, startAngle et endAngle les angles de départ et d’arrivée (en radians) et counterClockwise un booléen qui sert à définir si l’arc est défini dans le sens antihoraire ou non. Étudions à présent le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
/*
* Retourne le contexte d'éxécution 2d du canvas
*/
function getContext() {
    return getCanvas().getContext("2d");
}
/*
* Retourne une référence à l'objet canvas crée à l'aide de la balise 
* placée dans le code html
*/
function getCanvas() {
    return document.querySelector("canvas");
}

function init() {
    let canvas = getCanvas();
    let context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "#f00f00";
    // on bouge notre premier point de chemin vers le centre du cercle
    context.moveTo(320, 240);
    // on dessine le cercle
    context.arc(320, 240, 50, 0, 360 * Math.PI / 180, false);
    //remplir le cercle
    context.fill();
}


window.addEventListener("load", init);

Comme pour la ligne, faisons le point de ce que nous avons actuellement sur notre canvas.

Un cercle dont les propriétés sont :

  • Une couleur de remplissage définie par la propriété fillStyle ;
  • Le centre de départ situé à x = 320px et y = 240px.
  • Un rayon de 50px.
  • Un angle de départ situé à 0 degrés et converti en radians.
  • Un angle d’arrivée situé à 360 degrés et converti en radians.
  • Une direction dans le sens horaire.

Comme vous pouvez le constater, nous utilisons toujours la méthode context.beginPath() pour créer un nouveau dessin. Afin de pouvoir exécuter le nouveau jeu d’instructions, relatif cette fois-ci à des formes pleines, nous utilisons la méthode context.fill() qui agit de la même façon que la méthode context.stroke().

En regardant un peu plus en avant l’API de dessin, on peut s’apercevoir qu’il existe pas mal de méthodes pour dessiner d’autres primitives, ou d’autres types de lignes, qu’elles soient droites ou dessinées à l’aide de courbes de Bézier etc… On va maintenant apprendre à dessiner un rectangle.

I-C-3. Dessiner un rectangle

Si je souhaite dessiner un rectangle, je peux utiliser la méthode suivante :

 
Sélectionnez
1.
context.fillRect(x, y, width, height);

Où x et y représentent les coordonnées du coin en haut à gauche de mon rectangle et width et height représentent la largeur et la hauteur du rectangle que je souhaite dessiner.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
/*
* Retourne le contexte d'éxécution 2d du canvas
*/
function getContext() {
    return getCanvas().getContext("2d");
}
/*
* Retourne une référence à l'objet canvas crée à l'aide de la balise 
* placée dans le code html
*/
function getCanvas() {
    return document.querySelector("canvas");
}

function init() {
    let canvas = getCanvas();
    let context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "#ff0000";
    //dessiner un rectangle
    context.fillRect(320,240,100,30);
    //remplir le cercle
    context.fill();
}


window.addEventListener("load", init);

Maintenant que nous savons comment dessiner des primitives, nous allons apprendre à les transformer, comprendre par là que nous allons leur appliquer un changement d’échelle, de rotation, d’alpha ou de translation.

I-D. Appliquer des transformations

video à placer Appliquons quelques transformations !

Appliquer une transformation en HTML5 est facile, en effet l’API met à notre disposition des méthodes simples, la seule difficulté réside dans le fait que ces méthodes sont cumulatives mais nous reviendrons là-dessus plus tard, pour l’instant nous allons nous contenter d’appliquer des transformations à un rectangle.

I-D-1. L’alpha

Pour dessiner quelque chose en transparence, on modifie la propriété :

 
Sélectionnez
1.
2.
// pour définir la transparence à 50%
context.globalAlpha = 0.5;

La valeur de la propriété globalAlpha du contexte se situe toujours entre 0 et 1, si, comme dans l’exemple ci-dessus, vous souhaitez obtenir un alpha de 50 %, il vous suffit de modifier la valeur de cette propriété à 0.5. Facile n’est-ce pas ? Par exemple si je veux dessiner un rectangle avec une transparence de 50 % mon code ressemblera à ceci :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
function init() {
    let canvas = getCanvas();
    let context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // on redéfinit l'opacité des dessins qui vont suivre
    context.globalAlpha = 0.5;
    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "#f00f00";
    //dessiner un rectangle
    context.fillRect(320,240,100,30);
    //remplir le rectangle
    context.fill();
}

On aperçoit bien le rectangle en transparence, essayez de jouer un peu avec les valeurs de l’alpha, c’est vraiment facile ! Vous avez sans doute remarqué que jusqu’ici nous avons défini les coordonnées de nos primitives à l’aide des paramètres fournis à cet effet, toutefois lors de vos futurs développements, vous verrez qu’il n’est pas forcément pratique de procéder de la sorte. Le mieux serait de pouvoir dessiner nos objets aux coordonnées 0, 0 et de les déplacer ensuite. Ça tombe plutôt bien, la prochaine transformation que je compte vous montrer est la translation.

I-D-2. La translation

Pour effectuer une translation, on utilise la méthode :

 
Sélectionnez
1.
context.translate( translateX, translateY );

translateX est le déplacement sur l’axe des x (en pixels) que vous souhaitez obtenir et translateY la même chose mais sur l’axe des y. Ainsi, pour dessiner un carré rouge de 100 pixels de côtés prenant son point d’origine aux coordonnées x = 47 et y = 72, j’aurai à écrire le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
function init() {
    let canvas = getCanvas();
    let context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // on déplace le point de départ des futurs dessins
    context.translate( 47, 72 );
    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "red";
    //dessiner un rectangle
    context.fillRect(0,0,100,100);
    //remplir le rectangle
    context.fill();
}

Notez que nous aurions tout aussi bien pu utiliser les deux premiers paramètres de la méthode fillRect ( ce que nous faisions jusqu’ici ), toutefois comme expliqué plus haut, il vous sera plus utile d’utiliser les méthodes de transformations par la suite plutôt que d’utiliser ce type de paramètre. Passons maintenant au changement d’échelle.

I-D-3. Le scale

Pour effectuer un changement d’échelle, on utilise la méthode :

 
Sélectionnez
1.
context.scale( scaleX, scaleY);

scaleX est l’échelle sur l’axe des x que vous souhaitez obtenir et scaleY la même chose mais sur l’axe des y. Ainsi, pour dessiner le même carré que dans l’exemple précédent mais à une échelle deux fois plus grande, nous aurons le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
function init() {
    let canvas = getCanvas();
    let context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // on déplace le point de départ des futurs dessins
    context.translate( 47, 72 );
    // Echelle 2 fois plus grande sur l'axe des x et des y
    context.scale( 2, 2 );
    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "red";
    //dessiner un rectangle
    context.fillRect(0,0,100,100);
    //remplir le rectangle
    context.fill();
}

Ainsi, nous obtenons un carré de 100 pixels de côtés mais dont l’échelle est de 2, et visuellement, j’ai un carré de 200 pixels de côtés. Quel est l’intérêt de cette méthode ? Pourquoi ne pas directement dessiner un carré de 200 pixels de côtés ? En plus on code moins !

Et bien l’intérêt principal est de ne pas avoir à recalculer la largeur et la hauteur d’un objet d’affichage à chaque fois que l’on souhaite changer son échelle, de plus, ces calculs sont simples à réaliser lorsque l’objet en question n’est pas en rotation, mais dès qu’il s’agit de calculer une largeur et une hauteur avec une rotation par dessus le marché, ça devient plus compliqué et plus coûteux en ressources. Passons maintenant à la dernière transformation, la rotation.

I-D-4. La rotation

Avant de commencer, il me faut éclaircir un point que nous avons omis de préciser jusque là : l’unité de mesure employée pour une rotation. En effet, alors que la plupart des gens calculent leurs angles en degrés, en programmation graphique il est de coutume d’employer le radian. La formule de conversion degrés/radians est la suivante :

 
Sélectionnez
1.
angle_radians = angle_degré * ( Math.PI / 180 );

Nous l’avons déjà utilisé plus haut pour définir les angles de départ et de fin de notre arc. Maintenant, nous savons que lorsqu’on parlera d’un angle, on s’exprimera par défaut en radians et si l’on change d’unité de mesure, je vous le préciserai alors. Bien, maintenant que tout le monde parle le même langage, laissez-moi vous présenter la méthode qui vous permettra d’appliquer une rotation à vos objets :

 
Sélectionnez
1.
context.rotate( angle_radian );

Assez simple n’est-ce pas ? Nous allons reprendre le code de tout à l’heure et ajouter une rotation de 37° à notre carré :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
function init() {
    let canvas = getCanvas();
    let context = getContext();

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // on déplace le point de départ des futurs dessins
    context.translate( 47, 72 );
    // Echelle 2 fois plus grande sur l'axe des x et des y
    context.scale( 2, 2 );
    // on applique une rotation de 37 degrés convertis en radians
    context.rotate( 37 * Math.PI / 180 );
    // crée un nouveau path 
    context.beginPath();
    // on va donc définir un style de remplissage
    context.fillStyle = "red";
    //dessiner un rectangle
    context.fillRect(0,0,100,100);
    //remplir le rectangle
    context.fill();
}

Notez que toutes les rotations s’effectuent dans le sens horaire ! Nous avons vu les transformations que nous voulions voir, nous y reviendrons plus tard. Il nous reste à voir le cumul des transformations, la sauvegarde et restauration du contexte et nous aurons terminé ce premier chapitre.

I-E. Cumul , save & restore

L’objet context utilise une matrice pour représenter et stocker le résultat de toutes les transformations qu’on lui applique. Nous ne nous étendrons pas pour l’instant sur ce qu’est une matrice ni comment l’utiliser, en revanche, sachez qu’une des lois basiques des calculs matriciels est la non commutativité. En clair, cela signifie que les transformations que l’on applique à une matrice se cumulent et que l’ordre dans lequel on les exécute influe sur le résultat obtenu.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
function init() {
    let canvas = getCanvas();
    let context = getContext();

    //on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    //ici on applique une translation AVANT le scale
    context.translate(47, 72);
    context.scale(2, 2);

    context.beginPath();
    context.fillStyle = "red";
    context.fillRect(0, 0, 100, 100);
    context.fill();

    //ici on applique une translation APRES le scale
    context.scale(2, 2);
    context.translate(47, 72);

    context.beginPath();
    context.fillStyle = "red";
    context.fillRect(0,0,100,100);
    context.fill();
}

On peut voir que le résultat obtenu à l’écran est différent suivant que l’on applique scale avant ou après la translation, tout cela est normal. Mais alors dans quel ordre appliquer mes transformations ? Et bien ça, c’est à vous de le décider, bien qu’en règle générale le résultat attendu nécessite que l’on applique dans l’ordre une translation, une rotation et enfin l’échelle.

Et vous pensiez que c’était fini ? Et bien non, en effet les transformations en html5 c’est pas de la tarte ( enfin uniquement quand on y est pas habitué, après je vous rassure ça roule tout seul ). Si je veux par exemple définir une échelle à 0,5 après avoir l’avoir définie à 2, le code suivant ne fonctionne pas :

 
Sélectionnez
1.
2.
3.
4.
5.
// l'échelle est à 2
context.scale(2,2);

// l'échelle vaut 1 car 2 * 0.5 = 1
context.scale(0.5, 0.5);

Le problème, c’est que je ne suis pas forcément au courant de l’état actuel de ma matrice au moment où je l’utilise, donc cela peut me poser pas mal de problèmes pour obtenir l’état désiré. Heureusement, il existe une parade à cela : la sauvegarde du contexte ! En effet, il est possible de stocker en mémoire l’état du contexte et de le restaurer par la suite. Cela fonctionne avec la paire de méthodes suivantes :

 
Sélectionnez
1.
2.
context.save()
context.restore()

La méthode, context.save() permet de sauvegarder l’état actuel du contexte, la méthode context.restore() permet quant à elle de restituer l’état du dernier contexte sauvegardé, c’est-à-dire que les données de transformations de la matrice ainsi que les données de dessins etc. seront exactement les mêmes que lors du dernier appel à context.save(). Ces méthodes fonctionnent un peu à la manière d’une pile d’assiettes, c’est-à-dire que le dernier contexte sauvegardé ira « au-dessus » de la pile et donc, lors du prochain appel à context.restore() ce sera cette dernière « assiette » qui sera restituée.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
function init(){
    let canvas = getCanvas();
    let context  = getContext(); 

    // on détermine les dimensions du canvas
    canvas.width = 640;
    canvas.height = 480;

    // sauvegarder le contexte = t1
    context.save();

    context.scale(0.5,0.5); 
    context.translate(10,10);
    // on applique une rotation de 45° au repère
    context.rotate(45 * Math.PI / 180);

    // sauvegarde le contexte tel qu'il est en t2
    context.save();

    // crée un nouveau "path"
    context.beginPath(); 
    // on va donc définir un style de remplissage
    context.fillStyle = "blue";
    // dessiner un carré
    context.fillRect(300,220, 100, 100);
    // remplir le cercle 
    context.fill();

    // on restaure le dernier contexte sauvegardé avec save();
    context.restore();

    // on redéfinit l'opacité des dessins qui vont suivre 
    context.globalAlpha = 0.5;

    // crée un nouveau "path"
    context.beginPath(); 
    // on va donc définir un style de remplissage
    context.fillStyle = "#ff0000";
    // dessiner un rectangle
    context.fillRect(320,240, 100, 30);
    // remplir le cercle 
    context.fill();
}

function getContext(){
    return getCanvas().getContext("2d");
}

function getCanvas(){
    return document.querySelector("canvas"); 
}

window.addEventListener("load", init);

Voilà, les bases des transformations et du dessin avec canvas sont posées, nous venons de clôturer ce premier chapitre.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2020 Nom Auteur. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.