I. Introduction▲
Les programmes de test sont présents dans le répertoire exemples accompagnant le tutoriel.
II. Les courbes de Bézier cubiques (cubic bezier)▲
II-A. Un exemple interactif▲
II-B. La production de listes de coordonnées▲
II-C. Les courbes de Bézier en action▲
III. Des classes pour les interpolations, fonctions d'easing et les courbes de Bézier cubiques▲
IV. Une unité pour les nostalgiques▲
[Exemple Interpolations 08]
Comme souvent en Pascal, il est possible de travailler avec des fonctions hors des classes. Nous fournissons donc une unité baptisée interpolationfunctions composée des fonctions étudiées, mais accessibles directement.
En voici l'interface :
unit
interpolationfunctions;
{$
mode
objfpc
}
{$
H+
}
interface
uses
Classes, SysUtils;
type
TInterpolationFunction = function
(AStart, AEnd, ADuration, AStep: Single
):
Single
;
function
InPower(AStart, AChange, ADuration, AStep: Single
; APower: Integer
): Single
;
function
OutPower(AStart, AChange, ADuration, AStep: Single
; APower: Integer
): Single
;
function
InOutPower(AStart, AChange, ADuration, AStep: Single
; APower: Integer
): Single
;
function
EaseNone(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInQuad(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutQuad(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutQuad(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInQuad(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInCubic(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutCubic(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutCubic(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInCubic(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInQuart(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutQuart(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutQuart(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInQuart(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInQuint(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutQuint(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutQuint(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInQuint(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInSine(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutSine(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutSine(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInSine(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInExpo(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutExpo(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutExpo(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInExpo(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInCirc(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutCirc(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutCirc(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInCirc(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInElastic(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutElastic(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutElastic(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInElastic(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInBack(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutBack(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutBack(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInBack(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInBounce(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutBounce(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseInOutBounce(AStart, AEnd, ADuration, AStep: Single
): Single
;
function
EaseOutInBounce(AStart, AEnd, ADuration, AStep: Single
): Single
;
Le type TInterpolationfunction permet d'utiliser ces fonctions comme paramètres de procédures ou d'autres fonctions.
Ainsi, l'entête de ComputeInterpolation pourra devenir :
function
TMainForm.ComputeInterpolation(AInter: TInterpolationFunction; AStart,
AEnd, AStep: Single
; ABack: Boolean
): Single
;
L'appel de cette fonction se fera en précisant qu'il ne s'agit pas d'exécuter la fonction fournie comme premier paramètre, mais d'accéder à son adresse. Nous utiliserons alors un @ devant son identificateur :
btnEaseInQuad.Left := ComputeInterpolationInt(@EaseInQuad, 0
, LWidth, Li, LBack);
Pour le reste, la modification essentielle tient au fait que la durée n'est plus accessible directement puisque nous nous situons hors de la classe : il faut par conséquent prévoir un paramètre supplémentaire à toutes nos fonctions (Aduration).
Si nous reprenons l'exemple 03 donné dans le premier tutoriel pour afficher les fonctions d'easing, quelques modifications sont à apporter à sa réalisation. Voici le code source de l'unité principale modifiée :
unit
main;
{$
mode
objfpc
}
{$
H+
}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
StdCtrls, ComCtrls,
interpolationfunctions;
const
C_DefaultDuration = 100
;
C_End = 200
;
type
{
TMainForm
}
TMainForm = class
(TForm)
btnEaseOutInQuart: TButton;
btnLinear2: TButton;
btnEaseInQuad: TButton;
btnEaseOutCubic: TButton;
btnEaseInCubic: TButton;
btnEaseOutQuad: TButton;
btnEaseInOutCubic: TButton;
btnEaseInOutQuart: TButton;
btnEaseOutInCubic: TButton;
btnEaseInQuart: TButton;
btnEaseOutQuart: TButton;
btnEaseInQuint: TButton;
btnEaseOutQuint: TButton;
btnEaseOutInQuint: TButton;
btnEaseInOutQuint: TButton;
btnEaseInSine: TButton;
btnEaseOutSine: TButton;
btnEaseInOutSine: TButton;
btnEaseOutInSine: TButton;
btnEaseInOutQuad: TButton;
btnEaseOutInQuad: TButton;
btnEaseInExpo: TButton;
btnEaseInCirc: TButton;
btnEaseInElastic: TButton;
btnEaseInBack: TButton;
btnEaseInBounce: TButton;
btnEaseOutCirc: TButton;
btnEaseOutElastic: TButton;
btnEaseOutBack: TButton;
btnEaseOutBounce: TButton;
btnEaseOutExpo: TButton;
btnLinear: TButton;
btnEaseOutInCirc: TButton;
btnEaseOutInElastic: TButton;
btnEaseOutInBack: TButton;
btnEaseOutInBounce: TButton;
btnEaseInOutExpo: TButton;
btnEaseInOutCirc: TButton;
btnEaseInOutElastic: TButton;
btnEaseInOutBack: TButton;
btnEaseOutInExpo: TButton;
btnEaseInOutBounce: TButton;
pnlRight: TPanel;
pnlLeft: TPanel;
pnlBottom: TPanel;
pnlMain: TPanel;
Splitter1: TSplitter;
tbarWait: TTrackBar;
procedure
btnEaseInOutQuadClick(Sender: TObject);
procedure
btnEaseInQuadClick(Sender: TObject);
procedure
btnEaseOutInQuadClick(Sender: TObject);
procedure
btnEaseOutQuadClick(Sender: TObject);
procedure
btnLinear2Click(Sender: TObject);
procedure
FormCreate(Sender: TObject);
procedure
tbarWaitChange(Sender: TObject);
private
fDuration: Cardinal
;
procedure
SetDuration(AValue: Cardinal
);
public
property
Duration: Cardinal
read
fDuration write
SetDuration default
C_DefaultDuration;
function
ComputeInterpolation(AInter: TInterpolationFunction;
AStart, AEnd, AStep: Single
; ABack: Boolean
= False
):
Single
;
function
ComputeInterpolationInt(AInter: TInterpolationFunction; AStart, AEnd,
AStep: Integer
; ABack: Boolean
= False
): Integer
;
end
;
var
MainForm: TMainForm;
implementation
{$
R
*.lfm
}
{
TMainForm
}
procedure
TMainForm.btnLinear2Click(Sender: TObject);
//
***
lancement
des
animations
***
var
Li, LWidth, LWidth2: Integer
;
LBack: Boolean
;
begin
tbarWait.Enabled := False
;
LWidth := pnlLeft.Width - btnLinear2.Width;
LWidth2 := pnlRight.Width - btnLinear.Width;
for
LBack := False
to
True
do
for
Li := 1
to
Duration do
begin
btnLinear.Left := ComputeInterpolationInt(@EaseNone, 0
, LWidth, Li, LBack);
btnLinear2.Left := ComputeInterpolationInt(@EaseNone, 0
, LWidth2, Li, LBack);
btnEaseInQuad.Left := ComputeInterpolationInt(@EaseInQuad, 0
, LWidth, Li, LBack);
btnEaseOutQuad.Left := ComputeInterpolationInt(@EaseOutQuad, 0
, LWidth, Li, LBack);
btnEaseInOutQuad.Left := ComputeInterpolationInt(@EaseInOutQuad, 0
, LWidth2, Li, LBack);
btnEaseOutInQuad.Left := ComputeInterpolationInt(@EaseOutInQuad, 0
, LWidth2, Li, LBack);
btnEaseInCubic.Left := ComputeInterpolationInt(@EaseInCubic, 0
, LWidth, Li, LBack);
btnEaseOutCubic.Left := ComputeInterpolationInt(@EaseOutCubic, 0
, LWidth, Li, LBack);
btnEaseInOutCubic.Left := ComputeInterpolationInt(@EaseInOutCubic, 0
, LWidth2, Li, LBack);
btnEaseOutInCubic.Left := ComputeInterpolationInt(@EaseOutInCubic, 0
, LWidth2, Li, LBack);
btnEaseInQuart.Left := ComputeInterpolationInt(@EaseInQuart, 0
, LWidth, Li, LBack);
btnEaseOutQuart.Left := ComputeInterpolationInt(@EaseOutQuart, 0
, LWidth, Li, LBack);
btnEaseInOutQuart.Left := ComputeInterpolationInt(@EaseInOutQuart, 0
, LWidth2, Li, LBack);
btnEaseOutInQuart.Left := ComputeInterpolationInt(@EaseOutInQuart, 0
, LWidth2, Li, LBack);
btnEaseInQuint.Left := ComputeInterpolationInt(@EaseInQuint, 0
, LWidth, Li, LBack);
btnEaseOutQuint.Left := ComputeInterpolationInt(@EaseOutQuint, 0
, LWidth, Li, LBack);
btnEaseInOutQuint.Left := ComputeInterpolationInt(@EaseInOutQuint, 0
, LWidth2, Li, LBack);
btnEaseOutInQuint.Left := ComputeInterpolationInt(@EaseOutInQuint, 0
, LWidth2, Li, LBack);
btnEaseInSine.Left := ComputeInterpolationInt(@EaseInSine, 0
, LWidth, Li, LBack);
btnEaseOutSine.Left := ComputeInterpolationInt(@EaseOutSine, 0
, LWidth, Li, LBack);
btnEaseInOutSine.Left := ComputeInterpolationInt(@EaseInOutSine, 0
, LWidth2, Li, LBack);
btnEaseOutInSine.Left := ComputeInterpolationInt(@EaseOutInSine, 0
, LWidth2, Li, LBack);
btnEaseInExpo.Left := ComputeInterpolationInt(@EaseInExpo, 0
, LWidth, Li, LBack);
btnEaseOutExpo.Left := ComputeInterpolationInt(@EaseOutExpo, 0
, LWidth, Li, LBack);
btnEaseInOutExpo.Left := ComputeInterpolationInt(@EaseInOutExpo, 0
, LWidth2, Li, LBack);
btnEaseOutInExpo.Left := ComputeInterpolationInt(@EaseOutInExpo, 0
, LWidth2, Li, LBack);
btnEaseInCirc.Left := ComputeInterpolationInt(@EaseInCirc, 0
, LWidth, Li, LBack);
btnEaseOutCirc.Left := ComputeInterpolationInt(@EaseOutCirc, 0
, LWidth, Li, LBack);
btnEaseInOutCirc.Left := ComputeInterpolationInt(@EaseInOutCirc, 0
, LWidth2, Li, LBack);
btnEaseOutInCirc.Left := ComputeInterpolationInt(@EaseOutInCirc, 0
, LWidth2, Li, LBack);
btnEaseInElastic.Left := ComputeInterpolationInt(@EaseInElastic, 0
, LWidth, Li, LBack);
btnEaseOutElastic.Left := ComputeInterpolationInt(@EaseOutElastic, 0
, LWidth, Li, LBack);
btnEaseInOutElastic.Left := ComputeInterpolationInt(@EaseInOutElastic, 0
, LWidth2, Li, LBack);
btnEaseOutInElastic.Left := ComputeInterpolationInt(@EaseOutInElastic, 0
, LWidth2, Li, LBack);
btnEaseInBack.Left := ComputeInterpolationInt(@EaseInBack, 0
, LWidth, Li, LBack);
btnEaseOutBack.Left := ComputeInterpolationInt(@EaseOutBack, 0
, LWidth, Li, LBack);
btnEaseInOutBack.Left := ComputeInterpolationInt(@EaseInOutBack, 0
, LWidth2, Li, LBack);
btnEaseOutInBack.Left := ComputeInterpolationInt(@EaseOutInBack, 0
, LWidth2, Li, LBack);
btnEaseInBounce.Left := ComputeInterpolationInt(@EaseInBounce, 0
, LWidth, Li, LBack);
btnEaseOutBounce.Left := ComputeInterpolationInt(@EaseOutBounce, 0
, LWidth, Li, LBack);
btnEaseInOutBounce.Left := ComputeInterpolationInt(@EaseInOutBounce, 0
, LWidth2, Li, LBack);
btnEaseOutInBounce.Left := ComputeInterpolationInt(@EaseOutInBounce, 0
, LWidth2, Li, LBack);
sleep(10
);
Repaint;
Application.ProcessMessages;
end
;
tbarWait.Enabled := True
;
end
;
procedure
TMainForm.btnEaseInQuadClick(Sender: TObject);
//
***
fonctions
IN
***
var
Li, LWidth: Integer
;
LBack: Boolean
;
begin
tbarWait.Enabled := False
;
LWidth := pnlLeft.Width - btnLinear2.Width;
for
LBack := False
to
True
do
for
Li := 1
to
fDuration do
begin
btnLinear.Left := ComputeInterpolationInt(@EaseNone, 0
, LWidth, Li, LBack);
btnEaseInQuad.Left := ComputeInterpolationInt(@EaseInQuad, 0
, LWidth, Li, LBack);
btnEaseInCubic.Left := ComputeInterpolationInt(@EaseInCubic, 0
, LWidth, Li, LBack);
btnEaseInQuart.Left := ComputeInterpolationInt(@EaseInQuart, 0
, LWidth, Li, LBack);
btnEaseInQuint.Left := ComputeInterpolationInt(@EaseInQuint, 0
, LWidth, Li, LBack);
btnEaseInSine.Left := ComputeInterpolationInt(@EaseInSine, 0
, LWidth, Li, LBack);
btnEaseInExpo.Left := ComputeInterpolationInt(@EaseInExpo, 0
, LWidth, Li, LBack);
btnEaseInCirc.Left := ComputeInterpolationInt(@EaseInCirc, 0
, LWidth, Li, LBack);
btnEaseInElastic.Left := ComputeInterpolationInt(@EaseInElastic, 0
, LWidth, Li, LBack);
btnEaseInBack.Left := ComputeInterpolationInt(@EaseInBack, 0
, LWidth, Li, LBack);
btnEaseInBounce.Left := ComputeInterpolationInt(@EaseInBounce, 0
, LWidth, Li, LBack);
sleep(10
);
pnlLeft.Repaint;
Application.ProcessMessages;
end
;
tbarWait.Enabled := True
;
end
;
procedure
TMainForm.btnEaseInOutQuadClick(Sender: TObject);
//
***
fonctions
INOUT
***
var
Li, LWidth2: Integer
;
LBack: Boolean
;
begin
tbarWait.Enabled := False
;
LWidth2 := pnlRight.Width - btnLinear.Width;
for
LBack := False
to
True
do
for
Li := 1
to
fDuration do
begin
btnLinear2.Left := ComputeInterpolationInt(@EaseNone, 0
, LWidth2, Li, LBack);
btnEaseInOutQuad.Left := ComputeInterpolationInt(@EaseInOutQuad, 0
, LWidth2, Li, LBack);
btnEaseInOutCubic.Left := ComputeInterpolationInt(@EaseInOutCubic, 0
, LWidth2, Li, LBack);
btnEaseInOutQuart.Left := ComputeInterpolationInt(@EaseInOutQuart, 0
, LWidth2, Li, LBack);
btnEaseInOutQuint.Left := ComputeInterpolationInt(@EaseInOutQuint, 0
, LWidth2, Li, LBack);
btnEaseInOutSine.Left := ComputeInterpolationInt(@EaseInOutSine, 0
, LWidth2, Li, LBack);
btnEaseInOutExpo.Left := ComputeInterpolationInt(@EaseInOutExpo, 0
, LWidth2, Li, LBack);
btnEaseInOutCirc.Left := ComputeInterpolationInt(@EaseInOutCirc, 0
, LWidth2, Li, LBack);
btnEaseInOutElastic.Left := ComputeInterpolationInt(@EaseInOutElastic, 0
, LWidth2, Li, LBack);
btnEaseInOutBack.Left := ComputeInterpolationInt(@EaseInOutBack, 0
, LWidth2, Li, LBack);
btnEaseInOutBounce.Left := ComputeInterpolationInt(@EaseInOutBounce, 0
, LWidth2, Li, LBack);
sleep(10
);
pnlRight.Repaint;
Application.ProcessMessages;
end
;
tbarWait.Enabled := True
;
end
;
procedure
TMainForm.btnEaseOutInQuadClick(Sender: TObject);
//
***
fonctions
OUTIN
***
var
Li, LWidth2: Integer
;
LBack: Boolean
;
begin
tbarWait.Enabled := False
;
LWidth2 := pnlRight.Width - btnLinear.Width;
for
LBack := False
to
True
do
for
Li := 1
to
fDuration do
begin
btnLinear2.Left := ComputeInterpolationInt(@EaseNone, 0
, LWidth2, Li, LBack);
btnEaseOutInQuad.Left := ComputeInterpolationInt(@EaseOutInQuad, 0
, LWidth2, Li, LBack);
btnEaseOutInCubic.Left := ComputeInterpolationInt(@EaseOutInCubic, 0
, LWidth2, Li, LBack);
btnEaseOutInQuart.Left := ComputeInterpolationInt(@EaseOutInQuart, 0
, LWidth2, Li, LBack);
btnEaseOutInQuint.Left := ComputeInterpolationInt(@EaseOutInQuint, 0
, LWidth2, Li, LBack);
btnEaseOutInSine.Left := ComputeInterpolationInt(@EaseOutInSine, 0
, LWidth2, Li, LBack);
btnEaseOutInExpo.Left := ComputeInterpolationInt(@EaseOutInExpo, 0
, LWidth2, Li, LBack);
btnEaseOutInCirc.Left := ComputeInterpolationInt(@EaseOutInCirc, 0
, LWidth2, Li, LBack);
btnEaseOutInElastic.Left := ComputeInterpolationInt(@EaseOutInElastic, 0
, LWidth2, Li, LBack);
btnEaseOutInBack.Left := ComputeInterpolationInt(@EaseOutInBack, 0
, LWidth2, Li, LBack);
btnEaseOutInBounce.Left := ComputeInterpolationInt(@EaseOutInBounce, 0
, LWidth2, Li, LBack);
sleep(10
);
pnlRight.Repaint;
Application.ProcessMessages;
end
;
tbarWait.Enabled := True
;
end
;
procedure
TMainForm.btnEaseOutQuadClick(Sender: TObject);
//
***
fonctions
OUT
***
var
Li, LWidth: Integer
;
LBack: Boolean
;
begin
tbarWait.Enabled := False
;
LWidth := pnlLeft.Width - btnLinear2.Width;
for
LBack := False
to
True
do
for
Li := 1
to
fDuration do
begin
btnLinear.Left := ComputeInterpolationInt(@EaseNone, 0
, LWidth, Li, LBack);
btnEaseOutQuad.Left := ComputeInterpolationInt(@EaseOutQuad, 0
, LWidth, Li, LBack);
btnEaseOutCubic.Left := ComputeInterpolationInt(@EaseOutCubic, 0
, LWidth, Li, LBack);
btnEaseOutQuart.Left := ComputeInterpolationInt(@EaseOutQuart, 0
, LWidth, Li, LBack);
btnEaseOutQuint.Left := ComputeInterpolationInt(@EaseOutQuint, 0
, LWidth, Li, LBack);
btnEaseOutSine.Left := ComputeInterpolationInt(@EaseOutSine, 0
, LWidth, Li, LBack);
btnEaseOutExpo.Left := ComputeInterpolationInt(@EaseOutExpo, 0
, LWidth, Li, LBack);
btnEaseOutCirc.Left := ComputeInterpolationInt(@EaseOutCirc, 0
, LWidth, Li, LBack);
btnEaseOutElastic.Left := ComputeInterpolationInt(@EaseOutElastic, 0
, LWidth, Li, LBack);
btnEaseOutBack.Left := ComputeInterpolationInt(@EaseOutBack, 0
, LWidth, Li, LBack);
btnEaseOutBounce.Left := ComputeInterpolationInt(@EaseOutBounce, 0
, LWidth, Li, LBack);
sleep(10
);
pnlLeft.Repaint;
Application.ProcessMessages;
end
;
tbarWait.Enabled := True
;
end
;
procedure
TMainForm.FormCreate(Sender: TObject);
//
***
création
de
la
fiche
***
begin
Duration := C_DefaultDuration;
tbarWait.Max := C_End;
tbarWait.Position := fDuration;
end
;
procedure
TMainForm.tbarWaitChange(Sender: TObject);
//
***
changement
de
la
vitesse
***
begin
Duration := tbarWait.Position;
end
;
procedure
TMainForm.SetDuration(AValue: Cardinal
);
//
***
détermination
de
la
durée
d'interpolation
***
begin
if
fDuration = AValue then
Exit;
fDuration := AValue;
end
;
function
TMainForm.ComputeInterpolation(AInter: TInterpolationFunction; AStart,
AEnd, AStep: Single
; ABack: Boolean
): Single
;
//
***
calcul
des
interpolations
***
begin
if
AStep < 0
then
Result := AStart
else
if
AStep > Duration then
Result := AEnd
else
Result := AInter(AStart, AEnd, fDuration, AStep);
if
ABack then
Result := AEnd - Result;
end
;
function
TMainForm.ComputeInterpolationInt(AInter: TInterpolationFunction;
AStart, AEnd, AStep: Integer
; ABack: Boolean
): Integer
;
//
***
interpolation
entière
***
begin
Result := Round(ComputeInterpolation(AInter, AStart, AEnd, AStep, ABack));
end
;
end
.
À l'exécution, le résultat est exactement le même que celui obtenu avec une inclusion dans la classe de la fiche principale du projet. Comme notre objectif était de créer des classes autonomes, nous n'irons pas plus loin dans l'exploration de cette voie qui privilégie la programmation structurée sans passer par des classes et des objets.
Si le titre de ce chapitre pouvait sembler ironique, la souplesse de Pascal qui accepte aussi bien une programmation séquentielle qu'une approche POO (Programmation Orientée Objet) permet à chacun de s'y retrouver et de ne pas forcément mettre en œuvre des outils lourds pour des résultats médiocres !