Les interpolations et fonctions d'easing avec Lazarus I - Introduction aux interpolations,
Un billet de Gilles VASSEUR

Le , par gvasseur58

0PARTAGES

Vous utilisez Windows 10 et les transitions entre les vignettes de l'accueil vous intriguent ? Vous connaissez les propriétés transition et animation de CSS et vous aimeriez en reproduire certains des effets en Pascal ? Vous êtes un adepte de JavaScript et vous pensez que seul jQuery Easing Plugin est capable de produire des animations dignes de ce nom ? Vous êtes un inconditionnel de FireMonkey et vous aimeriez étendre des fonctionnalités de type animation à des composants de la VCL de Delphi ? Tout simplement, vous souhaitez rendre plus vivantes vos présentations ? Avec la nouvelle série de billets introduite ici, Lazarus sera pourvu de classes qui vous aideront à répondre à ces besoins ou interrogations.

Le projet

Free Pascal et Lazarus ne connaissent pas par défaut les fonctions d'interpolation pourtant utiles dès qu'un processus n'est pas linéaire. Les billets qui vont venir proposeront une étude des fonctions d'interpolation et une implémentation des courbes d'easing dont disposent d'autres langages. Après la construction de classes dédiées, une solution alternative à base de courbes de Bézier cubiques viendra clore la série.

Introduction aux interpolations

Une interpolation est « une opération mathématique permettant de construire une courbe à partir de la donnée d'un nombre fini de points, ou une fonction à partir de la donnée d'un nombre fini de valeurs ». En ce qui concerne notre sujet d'étude, il faudra prévoir l'évolution d'une fonction pour une durée voulue, depuis un point d'origine et jusqu'à un point d'arrivée. Autrement dit, il s'agira d'appliquer des formules mathématiques personnalisées à des animations : toute valeur évoluant dans le temps pourra être soumise à nos fonctions et classes.

À partir de cet objectif, nous pouvons déjà dégager les données nécessaires à sa réalisation :

  • un pas (ou fraction de temps) ;
  • une durée ;
  • une valeur initiale ;
  • une valeur finale.


Dans sa forme la plus élémentaire, notre travail produira une interpolation linéaire simple : nous utiliserons une suite arithmétique de raison égale à 1 divisé par le nombre total de pas. Ainsi, pour un début fixé à 0 et une fin fixée à 5, nous aurons une raison de 1/5 (= 0,2) et par conséquent la progression suivante :

Valeur de la variable de boucle Progression
0 → 0/5 = 0
1 → 1/5 = 0,2
2 → 2/5 = 0,4
3 → 3/5 = 0,6
4 → 4/5 = 0,8
5 → 5/5 = 1

Si nous prenons comme abscisses les valeurs de la boucle et comme ordonnées les valeurs de progression, nous obtenons un segment de la droite d'équation y = 1/5 x. Un intervalle qui commencerait à la valeur s créerait une droite d'équation y = 1/5 x + s. On ne peut pas imaginer plus simple, plus ordinaire... et plus monotone !

L'idée essentielle du travail à réaliser consiste à soumettre ce déroulement à des fonctions qui ne produisent pas cette suite arithmétique, mais qui aboutissent à la même valeur finale (1 ou 100%).

Par exemple, si nous considérons une interpolation qui calcule des valeurs à partir du carré de la valeur de de la variable de boucle, nous obtenons le tableau suivant :

Valeur de la variable de boucle Progression
0 → (0/5)² = 0
1 → (1/5)² = 0,04
2 → (2/5)² = 0,16
3 → (3/5)² = 0,36
4 → (4/5)² = 0,64
5 → (5/5)² = 1

Nous avons bien les mêmes valeurs aux extrémités : 0 et 1. Ces valeurs signifient qu'au bout du compte, que ce soit avec la suite arithmétique ou avec cette formule qui utilise un carré, nous partons du même point d'origine pour arriver au même point final pendant la même durée : seules varient les valeurs intermédiaires.

Qu'apporte ce changement ? À examiner les tableaux, nous nous rendons compte que la progression avec le carré prend du retard sur celle qui est linéaire, mais qu'elle l'a totalement rattrapée à la fin de la boucle.

Voici les courbes représentatives des deux fonctions sur l'intervalle [0 , 1] :


D'un point de vue mathématique, ces résultats s'expliquent facilement : multiplier par lui-même, un nombre positif plus petit que 1 donne un nombre plus petit que le nombre initial ! C'est l'explication du retard. En revanche, ce retard est de moins en moins vrai et disparaît dès que 1 est atteint. L'effet obtenu sera donc celui d'une accélération.

Plus le degré de l'expression sera élevé, plus l'effet sera perceptible : avec des cubes, l'inertie de départ sera encore plus forte, mais le résultat sera au final le même, à savoir 1.

Nous verrons que les degrés des puissances ne sont pas les seuls outils à notre disposition : en fait, toute fonction ou combinaison de fonctions qui retourne des valeurs qui partent de 0 pour aboutir à 1 (ou 100%) est une candidate sérieuse à la réalisation d'une interpolation.

Nous pouvons évidemment viser d'autres effets qu'une accélération positive : une décélération est elle aussi tout à fait envisageable.

Dans le prochain billet, je vous proposerai une approche empirique de nos premières interpolations. En attendant, imprégnez-vous des quelques notions entrevues ici !

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de circular17
Membre confirmé https://www.developpez.com
Le 12/01/2019 à 16:07
Intéressant.

L'approche itérative constitue effectivement à avoir un pas de 1 et d'avoir une fonction de N dans R.

Il y a un écueil cependant selon le temps d'affichage d'un écran (d'une frame). Si ce temps varie, la transition ne correspondra pas à celle voulu et selon les machines cela peut aussi varier la vitesse générale.

Une façon de contrer cela est d'utiliser un grain de temps. L'idée est d'avoir un accumulateur de temps passé. Il suffit de l'incrémenter comme cela :

Code : Sélectionner tout
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
const 
  DureeGrainDeTemps = 15/(24*60*60*1000); //millisecondes
var
  TempsPrecedent, CumulTemps : TDateTime;
  TempsGranulaire : integer;

//initialisation
begin
  TempsPrecedent := 0;
  CumulTemps := 0;
  TempsGranulaire := 0;
  //...
end;

//dessin
var NouveauTemps, NbGrains : TDateTime;
begin
  NouveauTemps := Now;
  if TempsPrecedent <> 0 then CumulTemps += NouveauTemps - TempsPrecedent;
  TempsPrecedent := NouveauTemps;
  NbGrains := Trunc(CumulTemps/DureeGrainDeTemps);
  CumulTemps -= NbGrains*DureeGrainDeTemps;
  Inc(TempsGranulaire, NbGrains);
  //utiliser TempsGranulaire pour le calcul de l'interpolation
end;
Avatar de gvasseur58
Responsable Delphi https://www.developpez.com
Le 14/01/2019 à 9:48
Excellent commentaire, comme d'habitude ! J'avais en tête ce problème, mais je voudrais me concentrer sur les fonctions à mettre en œuvre sans considérer tout de suite le délicat problème de l'écoulement du temps dans un processus. C'est peut-être une idée à revoir : je vais y réfléchir !
Contacter le responsable de la rubrique Accueil

Partenaire : Hébergement Web